Sections

Abstract

In this document, I want to provide a write-up of how this model is similar to and different from the versions previously implemented. This model is meant to provide assembly of multiple sites at the same time, that may, or may not, be connected in some fashion. Along the way, I will mention some of the input and output format that I am expect as documentation. At the end, I will be presenting some preliminary results. I especially want to use this as a vehicle to think about how to analyse those results.

Functions

library(dplyr)     # Data Manipulation
Warning: package ‘dplyr’ was built under R version 4.1.1

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggplot2)   # 2-D Plot
Warning: package ‘ggplot2’ was built under R version 4.1.1
library(plotly)    # 3-D Plot
Warning: package ‘plotly’ was built under R version 4.1.1
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library(ggfortify) # used for biplots of PCAs
Warning: package ‘ggfortify’ was built under R version 4.1.1
library(vegan)     # Ecological analysis mega-package
Warning: package ‘vegan’ was built under R version 4.1.1
Loading required package: permute
Warning: package ‘permute’ was built under R version 4.1.1
Loading required package: lattice
This is vegan 2.5-7
library(RMTRCode2)

# https://stackoverflow.com/a/7172832
ifrm <- function(obj, env = globalenv()) {
    obj <- deparse(substitute(obj))
    if(exists(obj, envir = env)) {
        rm(list = obj, envir = env)
    }
}

Results, No Spatial Structure

First, we load up the preliminary results. In this case, we are loading a system in which 34 basal species and 66 consumer species form the pool for 10 unconnected environments. The pool and interaction matrix were assembled with the default parameters from Law and Morton’s 1996 work.

load(file.path(
  "..", "experiments", "MNA-FirstAttempt-Result-Env10-None.RData")
)
toRemove <- result$Abundance <= result$Parameters$EliminationThreshold
result$Abundance[toRemove] <- 0
ifrm(toRemove)

Events

In total, 9320 events were used in these environments, with the species and environment invasion both randomly assigned. The number of arrival and extinction events were controlled to both be half of this number. We chose this number due to the coupon collecting problem. In particular, we use the result that the probability of encountering each species is bounded: \[\text{Pr}(\text{Draws} < n \log_{e} n + c n) \rightarrow \exp(-\exp(-c)) \text{ as } n \rightarrow \infty\] where \(n\) is the number of species and \(c\) is a constant. For our purposes, we choose \(c = 5\) so that we have a probability of about \(99.3\%\) of seeing each species in each environment. In practice, we failed to observe 2 species-environment combinations. Notably, nearly every species had at least one successful invasion; 17 did not.

The initial abundance was set to be 4000 times the elimination threshold, in line with work on minimum viable populations (Traill et al. 2007). The elimination threshold is admittedly more arbitrary, since it sets an effective individual-area relationship. For this calculation, I set it to \(10^{-4}\), in line with our previous calculations. This is large enough to avoid numerical difficulties from precision, while being low enough to represent a decent sized region.

Since each population is assembling simultaneously, I chose to use exponential waiting times for the inter-species arrival and extinction times. Note that these rates are shared between species and environments, but arrival and extinction are fully independent of each other. Species and environment affected in each event was chosen uniformly at random. The question then is how to set the rate.

To set the rate in this case, I chose to set it to the largest eigenvalue magnitude of the per-environment interaction matrices. This magnitude corresponds to the strongest response we can see from the interaction matrix and, if the interaction matrix is a good approximation for the Jacobian around a stable fixed point (which is not guaranteed), indicates the characteristic time scale of the decay to equilibrium. Hence, (overall) arrival rates and (overall) extinction rates should happen on the same timescale as the (largest) dynamics in the system. Since there are 10 environments, we should then expect that 10 characteristic time scales, on average, should occur in between arrival events in the same environment.

Abundance

With 10 environments, it is probably not helpful to check 10 individual abundance curves, but looking at the first one might be helpful.

LawMorton1996_PlotAbundance(result$Abundance[seq(from = 1, 
                                                 to = nrow(result$Abundance), 
                                                 by = 10), c(1, 2:101)]) -> obj;
obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: Transformation introduced infinite values in continuous y-axis

Every vertical line is a species introduction or extinction by neutral dynamics.

Alpha Diversity plots

Intro

Perhaps more intriguing might be some sense of the biodiversity that we have in each system. We break the abundance results up by environment, then calculate the number of non-zero abundance curves at each time point. We also calculate the Shannon entropy (reminder: higher entropy means more uncertainty which means a flatter distribution).

Diversity <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    richness <- rowSums(env != 0)
    abundSum <- rowSums(env)
    entropy <- env / abundSum
    entropy <- - apply(
      entropy, MARGIN = 1,
      FUN = function(x) {
        sum(ifelse(x != 0, x * log(x), 0))
      })
    species <- apply(
      env, MARGIN = 1,
      FUN = function(x) {
        toString(which(x > 0))
      }
    )
    data.frame(Time = time, 
               Richness = richness, 
               Entropy = entropy,
               Species = species,
               Environment = i)
  },
  abund = result$Abundance,
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)


Diversity <- dplyr::bind_rows(Diversity)
Diversity <- Diversity %>% dplyr::mutate(
  Evenness = Entropy / log(Richness)
)
Richness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Richness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Richness = mean(Richness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Richness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

So richness hovers around a similar regime throughout the majority of the simulation. Note in this plot that we have emphasised one environmental curve and superimposed the mean in black. We do manage to reach heights of 11 species in one environment, but these heights are shortlived. Instead, we seem to observe a (time and environment averaged) value:

mean(Diversity$Richness)
[1] 4.458893

(If we consider the first 10,000 time units as burn-in, we instead see a value of

mean(Diversity$Richness[Diversity$Time > 10000])
[1] 4.587217
Entropy
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Entropy,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Entropy = mean(Entropy)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Entropy
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 8043 row(s) containing missing values (geom_path).
Warning: Removed 2297 row(s) containing missing values (geom_path).

Entropy has a similar, but highly erratic, behaviour. If one tries to follow one of the entropy curves, then one sees that they have fairly substantial periods of almost smooth behaviour followed by suddenly very noisy behaviour, and noise seems to be the dominant mode if one tries to examine the low alpha environment in the background. There are some easy to make predictions. Extinctions reduce the entropy in the system, as you become more certain about what remains. Analogously, arrivals increase the entropy. We can probably better see the relationship beyond these principles by plotting entropy against richness and connecting observations by environment and time.

Entropy-Richness
# ggplot2::ggplot(
#   Diversity %>% dplyr::filter(Environment < 4), 
#   ggplot2::aes(
#     x = Richness,
#     y = Entropy,
#     group = Environment,
#     color = Time
#   )
# ) + ggplot2::geom_path(
# ) + ggplot2::guides(
#   alpha = "none"
# )

plotly::plot_ly(data = Diversity %>% dplyr::filter(Environment < 2),
                x = ~Richness, y = ~Entropy, z = ~Time, type = "scatter3d",
                mode = "lines", opacity = 1, line = list(color = ~Time))

It seems quite hard to tell, but there does not appear to be any particular orientation (clockwise, counter-clockwise) or similar pattern here.

Evenness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Evenness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Evenness = mean(Evenness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Evenness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 10986 row(s) containing missing values (geom_path).
Warning: Removed 2831 row(s) containing missing values (geom_path).

Evenness helps highlight that this is a high variance process but with a relatively constrained mean.

Environment Diversity

We can, of course, flip the idea on its head. Instead of examining the diversity of species within environments, we can look at the diversity of environments occupied by species. Since very few species end up occupying environments, we just look at richness. Unfortunately, this is quite a memory exhaustive task.

EnvDiversity <- lapply(
    1:((ncol(result$Abundance) - 1) / result$NumEnvironments),
    function(i, abund, numSpecies) {
        time <- abund[, 1]
        env <- abund[, 1 + i + numSpecies * (1:result$NumEnvironments - 1)]
        richness <- rowSums(env != 0)
        abundSum <- rowSums(env)
        environments <- apply(
            env, MARGIN = 1,
            FUN = function(x) {
                toString(which(x > 0))
            }
        )
        data.frame(Time = time, 
                   Richness = richness, 
                   Abundance = abundSum,
                   Species = i,
                   Environments = environments)
    },
    abund = result$Abundance,
    numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

EnvDiversity <- dplyr::bind_rows(EnvDiversity)
ggplot2::ggplot(
  EnvDiversity %>% dplyr::filter(Richness > 1), 
  aes(x = Time, y = Richness, color = Species)
) + geom_point(
  alpha = 0.01, size = 3
) + guides(
  color = "none"
)

One immediately interesting trend here is that very few species are present across more than 5 environments at a given time. Indeed, only

unique(EnvDiversity$Species[EnvDiversity$Richness > 5])
[1] 12 14 22 30

are ever present in more than 5 environments at once. We can also examine how long these periods occur for by species by tabulation. We block times so that entries are all of the same unit length, and round the average richness during the given time unit.

with(EnvDiversity %>% mutate(
  Time = floor(Time)
  ) %>% group_by(
    Time, Species
    ) %>% summarise(
      Richness = round(mean(Richness)),
      .groups = "drop"
      ),
     table(Species, Richness))
       Richness
Species     0     1     2     3     4     5     6     7
    1   65970 14384   439     0     0     0     0     0
    2   34776 31378 11352  3287     0     0     0     0
    3   63206 13000  1767  2820     0     0     0     0
    4   46367 21078 11443  1617   288     0     0     0
    5   30648 31553 16542  1341   709     0     0     0
    6   72061  8732     0     0     0     0     0     0
    7   75522  5271     0     0     0     0     0     0
    8   64759 15508   526     0     0     0     0     0
    9   77919  2874     0     0     0     0     0     0
    10  17817 38679 20192  4105     0     0     0     0
    11  52666 24892  3235     0     0     0     0     0
    12   9669  2676  9781 26641 18597  9922  2294  1213
    13  64640 15703   450     0     0     0     0     0
    14  12094 21314 14556 16445 15148   931   305     0
    15  68564 10732  1497     0     0     0     0     0
    16  69690 10903   200     0     0     0     0     0
    17  30636 33714 15505   938     0     0     0     0
    18  69214 11322   257     0     0     0     0     0
    19  62460 17599   734     0     0     0     0     0
    20  74867  3685  1849   392     0     0     0     0
    21  47906 25702  4185  3000     0     0     0     0
    22   3180 13477 20151 21766 16740  5305   174     0
    23  65137 14940   716     0     0     0     0     0
    24  65792 12313  2688     0     0     0     0     0
    25  66085 11109  3599     0     0     0     0     0
    26  41654 19402 19201   536     0     0     0     0
    27  48808 20447 10279  1259     0     0     0     0
    28  68983 11810     0     0     0     0     0     0
    29  34631 26993 17044  2125     0     0     0     0
    30   5561 19065 20682 11364 11597  7963  3301  1260
    31  65019 15665   109     0     0     0     0     0
    32  19327 26015 29600  5851     0     0     0     0
    33  15128 23405 24345 10059  7856     0     0     0
    34  76432  4361     0     0     0     0     0     0
    35  73829  6080   884     0     0     0     0     0
    36  71050  9743     0     0     0     0     0     0
    37  38199 22369 15663  4486    76     0     0     0
    38  28752 35327 16714     0     0     0     0     0
    39  77008  3785     0     0     0     0     0     0
    40  49953 27129  3711     0     0     0     0     0
    41  79838   955     0     0     0     0     0     0
    42  78312  2481     0     0     0     0     0     0
    43  32813 23892 17056  6661   371     0     0     0
    44  39700 23980 15554  1559     0     0     0     0
    45  80793     0     0     0     0     0     0     0
    46  48461 27669  4663     0     0     0     0     0
    47  12053 36783 14826 15182  1949     0     0     0
    48  63151 16980   662     0     0     0     0     0
    49  73125  7668     0     0     0     0     0     0
    50  79860   933     0     0     0     0     0     0
    51  80255   538     0     0     0     0     0     0
    52  73946  6847     0     0     0     0     0     0
    53  77581  3212     0     0     0     0     0     0
    54  76399  4394     0     0     0     0     0     0
    55  55217 23951  1625     0     0     0     0     0
    56  72564  8229     0     0     0     0     0     0
    57  57443 22644   706     0     0     0     0     0
    58  78988  1805     0     0     0     0     0     0
    59  35315 37942  7536     0     0     0     0     0
    60  67881 12912     0     0     0     0     0     0
    61  52226 24749  3818     0     0     0     0     0
    62  62517 16663  1613     0     0     0     0     0
    63  46937 31441  2415     0     0     0     0     0
    64  68974 11213   606     0     0     0     0     0
    65  80793     0     0     0     0     0     0     0
    66  67130 12561  1102     0     0     0     0     0
    67  37887 27036 11834  4036     0     0     0     0
    68  51139 27047  2205   402     0     0     0     0
    69  61791 16492  2510     0     0     0     0     0
    70  73231  7354   208     0     0     0     0     0
    71  22117 22512 20026 14172  1966     0     0     0
    72  10060 26081 33450  7590  3612     0     0     0
    73  73696  7097     0     0     0     0     0     0
    74  80793     0     0     0     0     0     0     0
    75  77528  3008   257     0     0     0     0     0
    76  34905 34766 10606   516     0     0     0     0
    77  54799 21256  4116   622     0     0     0     0
    78  57557 21260  1976     0     0     0     0     0
    79  46966 28430  5397     0     0     0     0     0
    80  46394 30478  3921     0     0     0     0     0
    81  80793     0     0     0     0     0     0     0
    82  72153  8640     0     0     0     0     0     0
    83  57732 21901  1160     0     0     0     0     0
    84  73061  7732     0     0     0     0     0     0
    85  78108  2685     0     0     0     0     0     0
    86  64841 15952     0     0     0     0     0     0
    87  75894  4488   411     0     0     0     0     0
    88  77088  3705     0     0     0     0     0     0
    89  48328 32465     0     0     0     0     0     0
    90  80793     0     0     0     0     0     0     0
    91  36647 30720 11100  1868   458     0     0     0
    92  54513 24626  1654     0     0     0     0     0
    93  73946  6847     0     0     0     0     0     0
    94  69274 10406  1113     0     0     0     0     0
    95  43213 20293 14643  2644     0     0     0     0
    96  65015 15778     0     0     0     0     0     0
    97  75428  5365     0     0     0     0     0     0
    98  65968 13004  1821     0     0     0     0     0
    99  78629  2164     0     0     0     0     0     0
    100 52405 15982  9604  2802     0     0     0     0

Beta Diversity

Principal Components Analysis

(For the desktop, the amount of data we generated is too much, so we need to reduce the amount of data we use. To do so we average over time blocks, here of length 100.)

AveragedAbundance <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)

We can perform a PCA and see if are data can be summarised by a small number of dimensions. As we shall see, constraining to the first 25 principal components does not harm the system much.

PCA <- prcomp(AveragedAbundance %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)

There is not actually a lot of dependence within the system it appears. Consider the amount of variance explained by the first six principal components (ordered, as is tradition, by amount of variation explained).

head(summary(PCA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.03749 0.03273 0.03095 0.02751 0.02501 0.02482 

The amount of explained variation is as follows.

sum(summary(PCA)$importance[2, ])
[1] 0.99994

The traditional biplot follows, but despite the seeming presence of patterns, so little of the variance is explained that is probably not worth further examination.

ggplot2::autoplot(PCA, loadings = TRUE, 
                  data = AveragedAbundance %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

We can try again with presence-absence data instead.

AveragedPA <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::mutate(
  dplyr::across(.cols = !time, .fns = ~ .x > 0)
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCAPA <- prcomp(AveragedPA %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCAPA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.05537 0.04584 0.04483 0.03901 0.03578 0.03410 

So it is a bit better, but not by much.

ggplot2::autoplot(PCAPA, loadings = TRUE, 
                  data = AveragedPA %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

I should note that this is not a failure of the method persay. What this says to me is that dimension reduction of the system cannot reduce the system to a human-readable set of descriptors. The system is still being reduced (from 100 Species * 10 Environments to 25 Components to cover

sum(summary(PCA)$importance[2, ])
[1] 0.99994

of the variation). My initial hypothesis for when the systems are coupled is that, as coupling increases, the number of principal components should decrease, since one system’s changes will be better predictors of the next system’s changes. (An interesting related question; do the number of principal components correlate with the amount of biodiversity in the system?

ifrm(AveragedAbundance)
ifrm(AveragedPA)
ifrm(PCA)
ifrm(PCAPA)
Beta Diversity Over Time

Since trying to reduce the system dimensionally does not work (well enough) a next attempt might be to consider how the pair-wise beta diversity changes over the course of the simulation. Initial problems include that there are quite a few measures of beta diversity as well as that the (square of the) number of environments will determine how many entries we need to consider.

To try this, we are going to reorganise our data into data frame with attributes of environment, time, and traits/species.

Environments <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    
    retval <- data.frame(Environment = toString(i),
                         Time = time
    )
    
    retval <- cbind(retval, env)
    colnames(retval) <- c("Environment", "Time", 
                          paste0("Basal", 1:34), 
                          paste0("Consumer", 35:100)
    )
    return(retval)
                          
  },
  abund = result$Abundance %>% data.frame(
  ) %>% dplyr::mutate(
    time = floor(time/100)*100
  ) %>% dplyr::group_by(
    time
  ) %>% dplyr::summarise(
    dplyr::across(.fns = ~ mean(.x))
  ),
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

Environments <- dplyr::bind_rows(Environments)
Environments <- Environments %>% dplyr::filter(
  dplyr::if_any(dplyr::starts_with(c("Basal", "Consumer")), ~ (.x != 0)),
)
Cluster Analysis Over Time

One perhaps interesting idea is to try to group the environments by cluster by considering the abundances (or presence-absence) of the species present as traits. The question then is whether the environments have a tendency to attract to specific points or if they wander around each other without relation. (The latter is more neutral.) If they appear to be convergent (which so far would seem to disagree mostly with the alpha diversity analyses), then that would imply that dynamics determine the majority of the system’s fate, while if they instead wander more randomly, then they would appear to be dominated by the neutral mechanisms. (Of course, this is affected by the rate of the neutral mechanisms, so it exists on a definite continuum.) (Note, of course, that cluster analysis usually includes something like PCA, so we would not necessarily expect a different result.)

My working hypothesis for how to use the distance measures is that we need a metric measure (Kindt and Coe, 2005) with any identification scrubbed off (site, time collected). Kindt and Coe suggest that taking the square root of Bray-Curtis makes it metric and thus usable for mathematical distance analyses (such as hierarchical clustering). The vegan package recomends using Jaccard instead of Bray-Curtis, for the same reason.

EnvironmentDistance <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard")
EnvDistClust <- hclust(EnvironmentDistance)
plot(EnvDistClust, labels = FALSE)

Note that are major breakdowns extremely early yielding a large number of clusters but there are expected to be 10 environments (if historical contingency mattered most) or a small number of clusters (if they are converging). The large number of clusters says to me that the neutral dynamics are jumping rapidly from cluster to cluster.

We can try again with presence absence instead.

EnvironmentDistancePA <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard", binary = TRUE)
EnvDistClustPA <- hclust(EnvironmentDistancePA)
plot(EnvDistClustPA, labels = FALSE)

Results, Line Spatial Structure

ifrm(Diversity)
ifrm(EnvDistClust)
ifrm(EnvDistClustPA)
ifrm(EnvDiversity)
ifrm(Environments)
ifrm(EnvironmentDistance)
ifrm(EnvironmentDistancePA)

We repeat the procedures above for the linear set. We use the same pool and interaction matrices, but change the space so that ``adjacent’’ communities can disperse (e.g. 1 <-> 2 <-> 3 <->…<-> 9 <-> 10). Similarly, the history is the same in terms of arrivals and extinctions.

load(file.path(
  "..", "experiments", "MNA-FirstAttempt-Result-Env10-Line.RData")
)
toRemove <- result$Abundance <= result$Parameters$EliminationThreshold
result$Abundance[toRemove] <- 0
ifrm(toRemove)

Abundance

With 10 environments, it is probably not helpful to check 10 individual abundance curves, but looking at the first one might be helpful.

LawMorton1996_PlotAbundance(result$Abundance[seq(from = 1, 
                                                 to = nrow(result$Abundance), 
                                                 by = 10), c(1, 2:101)]) -> obj;
obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: Transformation introduced infinite values in continuous y-axis

Every vertical line is a species introduction or extinction by neutral dynamics. Note that, due to dispersal, these should lose some of their abundance to spreading, but if that spreading is faster than the population can recoup its losses, than it can invade and be wiped out (since invadability does not consider spatial dynamics, only local ecological dynamics). The primary notes then are that there are a large number more events appearing to occur (due to the spatial arrangement), but the system as a whole is stabler (since the species that get knocked out can be recaptured from space as well).

Alpha Diversity plots

Intro
Diversity <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    richness <- rowSums(env != 0)
    abundSum <- rowSums(env)
    entropy <- env / abundSum
    entropy <- - apply(
      entropy, MARGIN = 1,
      FUN = function(x) {
        sum(ifelse(x != 0, x * log(x), 0))
      })
    species <- apply(
      env, MARGIN = 1,
      FUN = function(x) {
        toString(which(x > 0))
      }
    )
    data.frame(Time = time, 
               Richness = richness, 
               Entropy = entropy,
               Species = species,
               Environment = i)
  },
  abund = result$Abundance,
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)


Diversity <- dplyr::bind_rows(Diversity)
Diversity <- Diversity %>% dplyr::mutate(
  Evenness = Entropy / log(Richness)
)
Richness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Richness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Richness = mean(Richness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Richness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

Entropy
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Entropy,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Entropy = mean(Entropy)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Entropy
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 1123 row(s) containing missing values (geom_path).
Warning: Removed 110 row(s) containing missing values (geom_path).

Entropy-Richness
plotly::plot_ly(data = Diversity %>% dplyr::filter(Environment < 2),
                x = ~Richness, y = ~Entropy, z = ~Time, type = "scatter3d",
                mode = "lines", opacity = 1, line = list(color = ~Time))
Evenness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Evenness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Evenness = mean(Evenness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Evenness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 1440 row(s) containing missing values (geom_path).
Warning: Removed 136 row(s) containing missing values (geom_path).

Environment Diversity
EnvDiversity <- lapply(
    1:((ncol(result$Abundance) - 1) / result$NumEnvironments),
    function(i, abund, numSpecies) {
        time <- abund[, 1]
        env <- abund[, 1 + i + numSpecies * (1:result$NumEnvironments - 1)]
        richness <- rowSums(env != 0)
        abundSum <- rowSums(env)
        environments <- apply(
            env, MARGIN = 1,
            FUN = function(x) {
                toString(which(x > 0))
            }
        )
        data.frame(Time = time, 
                   Richness = richness, 
                   Abundance = abundSum,
                   Species = i,
                   Environments = environments)
    },
    abund = result$Abundance,
    numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

EnvDiversity <- dplyr::bind_rows(EnvDiversity)
ggplot2::ggplot(
  EnvDiversity %>% dplyr::filter(Richness > 1), 
  aes(x = Time, y = Richness, color = Species)
) + geom_point(
  alpha = 0.01, size = 3
) + guides(
  color = "none"
)

with(EnvDiversity %>% mutate(
  Time = floor(Time)
  ) %>% group_by(
    Time, Species
    ) %>% summarise(
      Richness = round(mean(Richness)),
      .groups = "drop"
      ),
     table(Species, Richness))
       Richness
Species     0     1     2     3     4     5     6     7     8     9    10
    1   80793     0     0     0     0     0     0     0     0     0     0
    2   80624     0     1     1     2     1     1     2     0     2   159
    3   80793     0     0     0     0     0     0     0     0     0     0
    4   80047     0     0     2     0     1     0     0     1     0   742
    5   79905     0     6     0     6     2     5     1     0    11   857
    6   80793     0     0     0     0     0     0     0     0     0     0
    7   80793     0     0     0     0     0     0     0     0     0     0
    8   80793     0     0     0     0     0     0     0     0     0     0
    9   80793     0     0     0     0     0     0     0     0     0     0
    10  80793     0     0     0     0     0     0     0     0     0     0
    11  80793     0     0     0     0     0     0     0     0     0     0
    12    680     0     0     0     0     0     0     0     0     1 80112
    13  80639     1     1     1     1     1     1     2     1     1   144
    14     84     0     0     0     0     0     0     0     0     0 80709
    15  80793     0     0     0     0     0     0     0     0     0     0
    16  80793     0     0     0     0     0     0     0     0     0     0
    17  80793     0     0     0     0     0     0     0     0     0     0
    18  80793     0     0     0     0     0     0     0     0     0     0
    19  80793     0     0     0     0     0     0     0     0     0     0
    20  80793     0     0     0     0     0     0     0     0     0     0
    21  80793     0     0     0     0     0     0     0     0     0     0
    22     62     0     0     0     0     0     0     0     0     1 80730
    23  80793     0     0     0     0     0     0     0     0     0     0
    24  80546     0     1     0     0     0     0     1     0     0   245
    25  80793     0     0     0     0     0     0     0     0     0     0
    26  80793     0     0     0     0     0     0     0     0     0     0
    27  80793     0     0     0     0     0     0     0     0     0     0
    28  80793     0     0     0     0     0     0     0     0     0     0
    29  80721     1     0     0     1     0     0     0     0     1    69
    30  69391     9     2     8     1     8     5     2     8    12 11347
    31  80793     0     0     0     0     0     0     0     0     0     0
    32    500     0     0     0     0     0     1     0     0     1 80291
    33  66491     5    15    10    14     2    14     5    14    17 14206
    34  80793     0     0     0     0     0     0     0     0     0     0
    35  80332     1     0     1     1     1     1     1     0     2   453
    36  80793     0     0     0     0     0     0     0     0     0     0
    37  46279     1     1     2     1     1     3     1     2     4 34498
    38  80793     0     0     0     0     0     0     0     0     0     0
    39  80793     0     0     0     0     0     0     0     0     0     0
    40  80793     0     0     0     0     0     0     0     0     0     0
    41  80793     0     0     0     0     0     0     0     0     0     0
    42  80793     0     0     0     0     0     0     0     0     0     0
    43  27630     2     2     0     4     0     0     1     4     7 53143
    44  60232     1     8     3     3     7     2     7     5    14 20511
    45  80793     0     0     0     0     0     0     0     0     0     0
    46  80793     0     0     0     0     0     0     0     0     0     0
    47  52191     5     8     3     7     3     7     5     9    12 28543
    48  80793     0     0     0     0     0     0     0     0     0     0
    49  80793     0     0     0     0     0     0     0     0     0     0
    50  80793     0     0     0     0     0     0     0     0     0     0
    51  80793     0     0     0     0     0     0     0     0     0     0
    52  80793     0     0     0     0     0     0     0     0     0     0
    53  80793     0     0     0     0     0     0     0     0     0     0
    54  80793     0     0     0     0     0     0     0     0     0     0
    55  78735     1     1     2     0     1     2     0     0     2  2049
    56  80793     0     0     0     0     0     0     0     0     0     0
    57  79020     0     0     1     0     0     0     0     1     0  1771
    58  80793     0     0     0     0     0     0     0     0     0     0
    59  80793     0     0     0     0     0     0     0     0     0     0
    60  80793     0     0     0     0     0     0     0     0     0     0
    61  80466     0     1     0     0     1     1     1     0     1   322
    62  80793     0     0     0     0     0     0     0     0     0     0
    63  80793     0     0     0     0     0     0     0     0     0     0
    64  80793     0     0     0     0     0     0     0     0     0     0
    65  80793     0     0     0     0     0     0     0     0     0     0
    66  80793     0     0     0     0     0     0     0     0     0     0
    67  80793     0     0     0     0     0     0     0     0     0     0
    68  79023     1     5     4     7     5     6     4     4     4  1730
    69  78135     2     2     1     1     1     6     1     2     5  2637
    70  80793     0     0     0     0     0     0     0     0     0     0
    71   1394     0     0     0     0     0     0     0     0     1 79398
    72    292     1     0     0     0     0     1     0     1     0 80498
    73  80793     0     0     0     0     0     0     0     0     0     0
    74  80793     0     0     0     0     0     0     0     0     0     0
    75  80793     0     0     0     0     0     0     0     0     0     0
    76  78676     1     4     1     4     0     6     1     3     6  2091
    77  79765     0     0     1     0     0     1     0     0     1  1025
    78  79253     0     0     1     0     1     0     0     0     0  1538
    79  80793     0     0     0     0     0     0     0     0     0     0
    80  80793     0     0     0     0     0     0     0     0     0     0
    81  80793     0     0     0     0     0     0     0     0     0     0
    82  80793     0     0     0     0     0     0     0     0     0     0
    83  80793     0     0     0     0     0     0     0     0     0     0
    84  80793     0     0     0     0     0     0     0     0     0     0
    85  80793     0     0     0     0     0     0     0     0     0     0
    86  64862     3     6     4     7     4     6     3     8     6 15884
    87  80793     0     0     0     0     0     0     0     0     0     0
    88  80793     0     0     0     0     0     0     0     0     0     0
    89  80793     0     0     0     0     0     0     0     0     0     0
    90  80793     0     0     0     0     0     0     0     0     0     0
 [ reached getOption("max.print") -- omitted 10 rows ]

Beta Diversity

Principal Components Analysis
AveragedAbundance <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCA <- prcomp(AveragedAbundance %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.19145 0.11089 0.10844 0.07943 0.05993 0.04530 
sum(summary(PCA)$importance[2, ])
[1] 0.99996
ggplot2::autoplot(PCA, loadings = TRUE, 
                  data = AveragedAbundance %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

AveragedPA <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::mutate(
  dplyr::across(.cols = !time, .fns = ~ .x > 0)
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCAPA <- prcomp(AveragedPA %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCAPA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.21750 0.12185 0.08092 0.05943 0.05215 0.04468 
ggplot2::autoplot(PCAPA, loadings = TRUE, 
                  data = AveragedPA %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

sum(summary(PCA)$importance[2, ])
[1] 0.99996
ifrm(AveragedAbundance)
ifrm(AveragedPA)
ifrm(PCA)
ifrm(PCAPA)
Beta Diversity Over Time
Environments <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    
    retval <- data.frame(Environment = toString(i),
                         Time = time
    )
    
    retval <- cbind(retval, env)
    colnames(retval) <- c("Environment", "Time", 
                          paste0("Basal", 1:34), 
                          paste0("Consumer", 35:100)
    )
    return(retval)
                          
  },
  abund = result$Abundance %>% data.frame(
  ) %>% dplyr::mutate(
    time = floor(time/100)*100
  ) %>% dplyr::group_by(
    time
  ) %>% dplyr::summarise(
    dplyr::across(.fns = ~ mean(.x))
  ),
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

Environments <- dplyr::bind_rows(Environments)
Environments <- Environments %>% dplyr::filter(
  dplyr::if_any(dplyr::starts_with(c("Basal", "Consumer")), ~ (.x != 0)),
)
Cluster Analysis Over Time
EnvironmentDistance <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard")
EnvDistClust <- hclust(EnvironmentDistance)
plot(EnvDistClust, labels = FALSE)

EnvironmentDistancePA <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard", binary = TRUE)
EnvDistClustPA <- hclust(EnvironmentDistancePA)
plot(EnvDistClustPA, labels = FALSE)

Results, Slow Line Spatial Structure

ifrm(Diversity)
ifrm(EnvDistClust)
ifrm(EnvDistClustPA)
ifrm(EnvDiversity)
ifrm(Environments)
ifrm(EnvironmentDistance)
ifrm(EnvironmentDistancePA)

We repeat the procedures above for the linear set. We use the same pool and interaction matrices, but change the space so that ``adjacent’’ communities can disperse (e.g. 1 <-> 2 <-> 3 <->…<-> 9 <-> 10). Similarly, the history is the same in terms of arrivals and extinctions.

load(file.path(
  "..", "experiments", "MNA-FirstAttempt1e+06-Result-Env10-Line.RData")
)
toRemove <- result$Abundance <= result$Parameters$EliminationThreshold
result$Abundance[toRemove[1:(length(toRemove)/2)]] <- 0
result$Abundance[toRemove[(length(toRemove)/2 + 1):length(toRemove)]] <- 0
ifrm(toRemove)

Abundance

With 10 environments, it is probably not helpful to check 10 individual abundance curves, but looking at the first one might be helpful.

LawMorton1996_PlotAbundance(result$Abundance[seq(from = 1, 
                                                 to = nrow(result$Abundance), 
                                                 by = 10), c(1, 2:101)]) -> obj;
obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: Transformation introduced infinite values in continuous y-axis

Alpha Diversity plots

Intro
Diversity <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    richness <- rowSums(env != 0)
    abundSum <- rowSums(env)
    entropy <- env / abundSum
    entropy <- - apply(
      entropy, MARGIN = 1,
      FUN = function(x) {
        sum(ifelse(x != 0, x * log(x), 0))
      })
    species <- apply(
      env, MARGIN = 1,
      FUN = function(x) {
        toString(which(x > 0))
      }
    )
    data.frame(Time = time, 
               Richness = richness, 
               Entropy = entropy,
               Species = species,
               Environment = i)
  },
  abund = result$Abundance,
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)


Diversity <- dplyr::bind_rows(Diversity)
Diversity <- Diversity %>% dplyr::mutate(
  Evenness = Entropy / log(Richness)
)
Richness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Richness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Richness = mean(Richness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Richness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
geom_path: Each group consists of only one observation. Do you need to adjust the group
aesthetic?

Entropy
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Entropy,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Entropy = mean(Entropy)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Entropy
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 197949 row(s) containing missing values (geom_path).
Warning: Removed 1 row(s) containing missing values (geom_path).

Entropy-Richness
plotly::plot_ly(data = Diversity %>% dplyr::filter(Environment < 2),
                x = ~Richness, y = ~Entropy, z = ~Time, type = "scatter3d",
                mode = "lines", opacity = 1, line = list(color = ~Time))
Evenness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Evenness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Evenness = mean(Evenness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Evenness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 294140 row(s) containing missing values (geom_path).
Warning: Removed 1 row(s) containing missing values (geom_path).

Environment Diversity
EnvDiversity <- lapply(
    1:((ncol(result$Abundance) - 1) / result$NumEnvironments),
    function(i, abund, numSpecies) {
        time <- abund[, 1]
        env <- abund[, 1 + i + numSpecies * (1:result$NumEnvironments - 1)]
        richness <- rowSums(env != 0)
        abundSum <- rowSums(env)
        environments <- apply(
            env, MARGIN = 1,
            FUN = function(x) {
                toString(which(x > 0))
            }
        )
        data.frame(Time = time, 
                   Richness = richness, 
                   Abundance = abundSum,
                   Species = i,
                   Environments = environments)
    },
    abund = result$Abundance,
    numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

EnvDiversity <- dplyr::bind_rows(EnvDiversity)
ggplot2::ggplot(
  EnvDiversity %>% dplyr::filter(Richness > 1), 
  aes(x = Time, y = Richness, color = Species)
) + geom_point(
  alpha = 0.01, size = 3
) + guides(
  color = "none"
)

with(EnvDiversity %>% mutate(
  Time = floor(Time)
  ) %>% group_by(
    Time, Species
    ) %>% summarise(
      Richness = round(mean(Richness)),
      .groups = "drop"
      ),
     table(Species, Richness))
       Richness
Species 0 1 4 5
    1   1 0 0 0
    2   1 0 0 0
    3   1 0 0 0
    4   1 0 0 0
    5   1 0 0 0
    6   1 0 0 0
    7   1 0 0 0
    8   1 0 0 0
    9   1 0 0 0
    10  1 0 0 0
    11  1 0 0 0
    12  0 0 0 1
    13  1 0 0 0
    14  0 0 1 0
    15  1 0 0 0
    16  1 0 0 0
    17  1 0 0 0
    18  1 0 0 0
    19  1 0 0 0
    20  1 0 0 0
    21  1 0 0 0
    22  0 0 0 1
    23  1 0 0 0
    24  1 0 0 0
    25  1 0 0 0
    26  1 0 0 0
    27  1 0 0 0
    28  1 0 0 0
    29  1 0 0 0
    30  1 0 0 0
    31  1 0 0 0
    32  0 1 0 0
    33  1 0 0 0
    34  1 0 0 0
    35  1 0 0 0
    36  1 0 0 0
    37  1 0 0 0
    38  1 0 0 0
    39  1 0 0 0
    40  1 0 0 0
    41  1 0 0 0
    42  1 0 0 0
    43  1 0 0 0
    44  1 0 0 0
    45  1 0 0 0
    46  1 0 0 0
    47  0 1 0 0
    48  1 0 0 0
    49  1 0 0 0
    50  1 0 0 0
    51  1 0 0 0
    52  1 0 0 0
    53  1 0 0 0
    54  1 0 0 0
    55  1 0 0 0
    56  1 0 0 0
    57  1 0 0 0
    58  1 0 0 0
    59  1 0 0 0
    60  1 0 0 0
    61  1 0 0 0
    62  1 0 0 0
    63  1 0 0 0
    64  1 0 0 0
    65  1 0 0 0
    66  1 0 0 0
    67  1 0 0 0
    68  1 0 0 0
    69  1 0 0 0
    70  1 0 0 0
    71  1 0 0 0
    72  0 1 0 0
    73  1 0 0 0
    74  1 0 0 0
    75  1 0 0 0
    76  1 0 0 0
    77  1 0 0 0
    78  1 0 0 0
    79  1 0 0 0
    80  1 0 0 0
    81  1 0 0 0
    82  1 0 0 0
    83  1 0 0 0
    84  1 0 0 0
    85  1 0 0 0
    86  1 0 0 0
    87  1 0 0 0
    88  1 0 0 0
    89  1 0 0 0
    90  1 0 0 0
    91  1 0 0 0
    92  1 0 0 0
    93  1 0 0 0
    94  1 0 0 0
    95  1 0 0 0
    96  1 0 0 0
    97  1 0 0 0
    98  1 0 0 0
    99  1 0 0 0
    100 1 0 0 0

Beta Diversity

Principal Components Analysis
AveragedAbundance <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCA <- prcomp(AveragedAbundance %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
Error in prcomp.default(AveragedAbundance %>% dplyr::select_if(~any(. >  : 
  cannot rescale a constant/zero column to unit variance
Beta Diversity Over Time
Cluster Analysis Over Time
LS0tDQp0aXRsZTogIk11bHRpcGxlIE51bWVyaWNhbCBBc3NlbWJseSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCi0tLQ0KDQojIFNlY3Rpb25zIHsudGFic2V0fQ0KDQojIyBBYnN0cmFjdA0KDQpJbiB0aGlzIGRvY3VtZW50LCBJIHdhbnQgdG8gcHJvdmlkZSBhIHdyaXRlLXVwIG9mIGhvdyB0aGlzIG1vZGVsIGlzIHNpbWlsYXIgdG8gYW5kIGRpZmZlcmVudCBmcm9tIHRoZSB2ZXJzaW9ucyBwcmV2aW91c2x5IGltcGxlbWVudGVkLg0KVGhpcyBtb2RlbCBpcyBtZWFudCB0byBwcm92aWRlIGFzc2VtYmx5IG9mIG11bHRpcGxlIHNpdGVzIGF0IHRoZSBzYW1lIHRpbWUsIHRoYXQgbWF5LCBvciBtYXkgbm90LCBiZSBjb25uZWN0ZWQgaW4gc29tZSBmYXNoaW9uLiANCkFsb25nIHRoZSB3YXksIEkgd2lsbCBtZW50aW9uIHNvbWUgb2YgdGhlIGlucHV0IGFuZCBvdXRwdXQgZm9ybWF0IHRoYXQgSSBhbSBleHBlY3QgYXMgZG9jdW1lbnRhdGlvbi4NCkF0IHRoZSBlbmQsIEkgd2lsbCBiZSBwcmVzZW50aW5nIHNvbWUgcHJlbGltaW5hcnkgcmVzdWx0cy4NCkkgZXNwZWNpYWxseSB3YW50IHRvIHVzZSB0aGlzIGFzIGEgdmVoaWNsZSB0byB0aGluayBhYm91dCBob3cgdG8gYW5hbHlzZSB0aG9zZSByZXN1bHRzLg0KDQojIyBGdW5jdGlvbnMNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikgICAgICMgRGF0YSBNYW5pcHVsYXRpb24NCmxpYnJhcnkoZ2dwbG90MikgICAjIDItRCBQbG90DQpsaWJyYXJ5KHBsb3RseSkgICAgIyAzLUQgUGxvdA0KbGlicmFyeShnZ2ZvcnRpZnkpICMgdXNlZCBmb3IgYmlwbG90cyBvZiBQQ0FzDQpsaWJyYXJ5KHZlZ2FuKSAgICAgIyBFY29sb2dpY2FsIGFuYWx5c2lzIG1lZ2EtcGFja2FnZQ0KbGlicmFyeShSTVRSQ29kZTIpDQoNCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9hLzcxNzI4MzINCmlmcm0gPC0gZnVuY3Rpb24ob2JqLCBlbnYgPSBnbG9iYWxlbnYoKSkgew0KICAgIG9iaiA8LSBkZXBhcnNlKHN1YnN0aXR1dGUob2JqKSkNCiAgICBpZihleGlzdHMob2JqLCBlbnZpciA9IGVudikpIHsNCiAgICAgICAgcm0obGlzdCA9IG9iaiwgZW52aXIgPSBlbnYpDQogICAgfQ0KfQ0KYGBgDQoNCg0KIyMgUmVzdWx0cywgTm8gU3BhdGlhbCBTdHJ1Y3R1cmUgey50YWJzZXR9DQoNCkZpcnN0LCB3ZSBsb2FkIHVwIHRoZSBwcmVsaW1pbmFyeSByZXN1bHRzLg0KSW4gdGhpcyBjYXNlLCB3ZSBhcmUgbG9hZGluZyBhIHN5c3RlbSBpbiB3aGljaCANCjM0IGJhc2FsIHNwZWNpZXMgYW5kIDY2IGNvbnN1bWVyIHNwZWNpZXMgZm9ybSB0aGUgcG9vbCBmb3IgMTAgdW5jb25uZWN0ZWQgZW52aXJvbm1lbnRzLg0KVGhlIHBvb2wgYW5kIGludGVyYWN0aW9uIG1hdHJpeCB3ZXJlIGFzc2VtYmxlZCB3aXRoIHRoZSBkZWZhdWx0IHBhcmFtZXRlcnMgZnJvbSBMYXcgYW5kIE1vcnRvbidzIDE5OTYgd29yay4NCg0KYGBge3J9DQpsb2FkKGZpbGUucGF0aCgNCiAgIi4uIiwgImV4cGVyaW1lbnRzIiwgIk1OQS1GaXJzdEF0dGVtcHQtUmVzdWx0LUVudjEwLU5vbmUuUkRhdGEiKQ0KKQ0KdG9SZW1vdmUgPC0gcmVzdWx0JEFidW5kYW5jZSA8PSByZXN1bHQkUGFyYW1ldGVycyRFbGltaW5hdGlvblRocmVzaG9sZA0KcmVzdWx0JEFidW5kYW5jZVt0b1JlbW92ZV0gPC0gMA0KaWZybSh0b1JlbW92ZSkNCmBgYA0KDQojIyMgRXZlbnRzDQpJbiB0b3RhbCwgYHIgbnJvdyhyZXN1bHQkRXZlbnRzKWAgZXZlbnRzIHdlcmUgdXNlZCBpbiB0aGVzZSBlbnZpcm9ubWVudHMsIHdpdGggdGhlIHNwZWNpZXMgYW5kIGVudmlyb25tZW50IGludmFzaW9uIGJvdGggcmFuZG9tbHkgYXNzaWduZWQuDQpUaGUgbnVtYmVyIG9mIGFycml2YWwgYW5kIGV4dGluY3Rpb24gZXZlbnRzIHdlcmUgY29udHJvbGxlZCB0byBib3RoIGJlIGhhbGYgb2YgdGhpcyBudW1iZXIuDQpXZSBjaG9zZSB0aGlzIG51bWJlciBkdWUgdG8gdGhlIGNvdXBvbiBjb2xsZWN0aW5nIHByb2JsZW0uDQpJbiBwYXJ0aWN1bGFyLCB3ZSB1c2UgdGhlIHJlc3VsdCB0aGF0IFt0aGUgcHJvYmFiaWxpdHkgb2YgZW5jb3VudGVyaW5nIGVhY2ggc3BlY2llcyBpcyBib3VuZGVkOl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ291cG9uX2NvbGxlY3RvciUyN3NfcHJvYmxlbSNFeHRlbnNpb25zX2FuZF9nZW5lcmFsaXphdGlvbnMpDQokJFx0ZXh0e1ByfShcdGV4dHtEcmF3c30gPCBuIFxsb2dfe2V9IG4gKyBjIG4pIFxyaWdodGFycm93IFxleHAoLVxleHAoLWMpKSBcdGV4dHsgYXMgfSBuIFxyaWdodGFycm93IFxpbmZ0eSQkDQp3aGVyZSAkbiQgaXMgdGhlIG51bWJlciBvZiBzcGVjaWVzIGFuZCAkYyQgaXMgYSBjb25zdGFudC4NCkZvciBvdXIgcHVycG9zZXMsIHdlIGNob29zZSAkYyA9IDUkIHNvIHRoYXQgd2UgaGF2ZSBhIHByb2JhYmlsaXR5IG9mIGFib3V0ICQ5OS4zXCUkIG9mIHNlZWluZyBlYWNoIHNwZWNpZXMgaW4gZWFjaCBlbnZpcm9ubWVudC4gDQpJbiBwcmFjdGljZSwgd2UgZmFpbGVkIHRvIG9ic2VydmUgYHIgc3VtKHdpdGgocmVzdWx0JEV2ZW50cywgdGFibGUoU3BlY2llcywgRW52aXJvbm1lbnQpKSA9PSAwKWAgc3BlY2llcy1lbnZpcm9ubWVudCBjb21iaW5hdGlvbnMuDQpOb3RhYmx5LCBuZWFybHkgZXZlcnkgc3BlY2llcyBoYWQgYXQgbGVhc3Qgb25lIHN1Y2Nlc3NmdWwgaW52YXNpb247DQpgciBzdW0ocm93U3Vtcyh3aXRoKHJlc3VsdCRFdmVudHMgJT4lIGZpbHRlcihUeXBlID09ICJBcnJpdmFsIiksIHRhYmxlKFNwZWNpZXMsIEVudmlyb25tZW50LCBTdWNjZXNzKSlbLCAsIDJdKSA9PSAwKWAgZGlkIG5vdC4NCg0KVGhlIGluaXRpYWwgYWJ1bmRhbmNlIHdhcyBzZXQgdG8gYmUgNDAwMCB0aW1lcyB0aGUgZWxpbWluYXRpb24gdGhyZXNob2xkLCBpbiBsaW5lIHdpdGggd29yayBvbiBtaW5pbXVtIHZpYWJsZSBwb3B1bGF0aW9ucyAoVHJhaWxsIGV0IGFsLiAyMDA3KS4NClRoZSBlbGltaW5hdGlvbiB0aHJlc2hvbGQgaXMgYWRtaXR0ZWRseSBtb3JlIGFyYml0cmFyeSwgc2luY2UgaXQgc2V0cyBhbiBlZmZlY3RpdmUgaW5kaXZpZHVhbC1hcmVhIHJlbGF0aW9uc2hpcC4NCkZvciB0aGlzIGNhbGN1bGF0aW9uLCBJIHNldCBpdCB0byAkMTBeey00fSQsIGluIGxpbmUgd2l0aCBvdXIgcHJldmlvdXMgY2FsY3VsYXRpb25zLg0KVGhpcyBpcyBsYXJnZSBlbm91Z2ggdG8gYXZvaWQgbnVtZXJpY2FsIGRpZmZpY3VsdGllcyBmcm9tIHByZWNpc2lvbiwgd2hpbGUgYmVpbmcgbG93IGVub3VnaCB0byByZXByZXNlbnQgYSBkZWNlbnQgc2l6ZWQgcmVnaW9uLg0KDQpTaW5jZSBlYWNoIHBvcHVsYXRpb24gaXMgYXNzZW1ibGluZyBzaW11bHRhbmVvdXNseSwgSSBjaG9zZSB0byB1c2UgZXhwb25lbnRpYWwgd2FpdGluZyB0aW1lcyBmb3IgdGhlIGludGVyLXNwZWNpZXMgYXJyaXZhbCBhbmQgZXh0aW5jdGlvbiB0aW1lcy4NCk5vdGUgdGhhdCB0aGVzZSByYXRlcyBhcmUgc2hhcmVkIGJldHdlZW4gc3BlY2llcyBhbmQgZW52aXJvbm1lbnRzLCBidXQgYXJyaXZhbCBhbmQgZXh0aW5jdGlvbiBhcmUgZnVsbHkgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlci4NClNwZWNpZXMgYW5kIGVudmlyb25tZW50IGFmZmVjdGVkIGluIGVhY2ggZXZlbnQgd2FzIGNob3NlbiB1bmlmb3JtbHkgYXQgcmFuZG9tLg0KVGhlIHF1ZXN0aW9uIHRoZW4gaXMgaG93IHRvIHNldCB0aGUgcmF0ZS4NCg0KVG8gc2V0IHRoZSByYXRlIGluIHRoaXMgY2FzZSwgSSBjaG9zZSB0byBzZXQgaXQgdG8gdGhlIGxhcmdlc3QgZWlnZW52YWx1ZSBtYWduaXR1ZGUgb2YgdGhlIHBlci1lbnZpcm9ubWVudCBpbnRlcmFjdGlvbiBtYXRyaWNlcy4NClRoaXMgbWFnbml0dWRlIGNvcnJlc3BvbmRzIHRvIHRoZSBzdHJvbmdlc3QgcmVzcG9uc2Ugd2UgY2FuIHNlZSBmcm9tIHRoZSBpbnRlcmFjdGlvbiBtYXRyaXggYW5kLCBpZiB0aGUgaW50ZXJhY3Rpb24gbWF0cml4IGlzIGEgZ29vZCBhcHByb3hpbWF0aW9uIGZvciB0aGUgSmFjb2JpYW4gYXJvdW5kIGEgc3RhYmxlIGZpeGVkIHBvaW50ICh3aGljaCBpcyBub3QgZ3VhcmFudGVlZCksIGluZGljYXRlcyB0aGUgY2hhcmFjdGVyaXN0aWMgdGltZSBzY2FsZSBvZiB0aGUgZGVjYXkgdG8gZXF1aWxpYnJpdW0uDQpIZW5jZSwgKG92ZXJhbGwpIGFycml2YWwgcmF0ZXMgYW5kIChvdmVyYWxsKSBleHRpbmN0aW9uIHJhdGVzIHNob3VsZCBoYXBwZW4gb24gdGhlIHNhbWUgdGltZXNjYWxlIGFzIHRoZSAobGFyZ2VzdCkgZHluYW1pY3MgaW4gdGhlIHN5c3RlbS4NClNpbmNlIHRoZXJlIGFyZSAxMCBlbnZpcm9ubWVudHMsIHdlIHNob3VsZCB0aGVuIGV4cGVjdCB0aGF0IDEwIGNoYXJhY3RlcmlzdGljIHRpbWUgc2NhbGVzLCBvbiBhdmVyYWdlLCBzaG91bGQgb2NjdXIgaW4gYmV0d2VlbiBhcnJpdmFsIGV2ZW50cyBpbiB0aGUgc2FtZSBlbnZpcm9ubWVudC4NCg0KIyMjIEFidW5kYW5jZSB7LnRhYnNldH0NCg0KV2l0aCAxMCBlbnZpcm9ubWVudHMsIGl0IGlzIHByb2JhYmx5IG5vdCBoZWxwZnVsIHRvIGNoZWNrIDEwIGluZGl2aWR1YWwgYWJ1bmRhbmNlIGN1cnZlcywgYnV0IGxvb2tpbmcgYXQgdGhlIGZpcnN0IG9uZSBtaWdodCBiZSBoZWxwZnVsLg0KYGBge3J9DQpMYXdNb3J0b24xOTk2X1Bsb3RBYnVuZGFuY2UocmVzdWx0JEFidW5kYW5jZVtzZXEoZnJvbSA9IDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gbnJvdyhyZXN1bHQkQWJ1bmRhbmNlKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAxMCksIGMoMSwgMjoxMDEpXSkgLT4gb2JqOw0KYGBgDQpgYGB7cn0NCm9iaiArIGdncGxvdDI6OnNjYWxlX3lfbG9nMTAoKSArIGdncGxvdDI6Omd1aWRlcyhjb2xvciA9IEZBTFNFKQ0KYGBgDQpFdmVyeSB2ZXJ0aWNhbCBsaW5lIGlzIGEgc3BlY2llcyBpbnRyb2R1Y3Rpb24gb3IgZXh0aW5jdGlvbiBieSBuZXV0cmFsIGR5bmFtaWNzLg0KDQojIyMjIEFscGhhIERpdmVyc2l0eSBwbG90cyB7LnRhYnNldH0NCg0KIyMjIyMgSW50cm8NClBlcmhhcHMgbW9yZSBpbnRyaWd1aW5nIG1pZ2h0IGJlIHNvbWUgc2Vuc2Ugb2YgdGhlIGJpb2RpdmVyc2l0eSB0aGF0IHdlIGhhdmUgaW4gZWFjaCBzeXN0ZW0uDQpXZSBicmVhayB0aGUgYWJ1bmRhbmNlIHJlc3VsdHMgdXAgYnkgZW52aXJvbm1lbnQsIHRoZW4gY2FsY3VsYXRlIHRoZSBudW1iZXIgb2Ygbm9uLXplcm8gYWJ1bmRhbmNlIGN1cnZlcyBhdCBlYWNoIHRpbWUgcG9pbnQuDQpXZSBhbHNvIGNhbGN1bGF0ZSB0aGUgU2hhbm5vbiBlbnRyb3B5IChyZW1pbmRlcjogaGlnaGVyIGVudHJvcHkgbWVhbnMgbW9yZSB1bmNlcnRhaW50eSB3aGljaCBtZWFucyBhIGZsYXR0ZXIgZGlzdHJpYnV0aW9uKS4NCmBgYHtyfQ0KRGl2ZXJzaXR5IDwtIGxhcHBseSgNCiAgMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzLA0KICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgIGVudiA8LSBhYnVuZFssIDEgKyAxOm51bVNwZWNpZXMgKyBudW1TcGVjaWVzICogKGkgLSAxKV0NCiAgICByaWNobmVzcyA8LSByb3dTdW1zKGVudiAhPSAwKQ0KICAgIGFidW5kU3VtIDwtIHJvd1N1bXMoZW52KQ0KICAgIGVudHJvcHkgPC0gZW52IC8gYWJ1bmRTdW0NCiAgICBlbnRyb3B5IDwtIC0gYXBwbHkoDQogICAgICBlbnRyb3B5LCBNQVJHSU4gPSAxLA0KICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICBzdW0oaWZlbHNlKHggIT0gMCwgeCAqIGxvZyh4KSwgMCkpDQogICAgICB9KQ0KICAgIHNwZWNpZXMgPC0gYXBwbHkoDQogICAgICBlbnYsIE1BUkdJTiA9IDEsDQogICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgIHRvU3RyaW5nKHdoaWNoKHggPiAwKSkNCiAgICAgIH0NCiAgICApDQogICAgZGF0YS5mcmFtZShUaW1lID0gdGltZSwgDQogICAgICAgICAgICAgICBSaWNobmVzcyA9IHJpY2huZXNzLCANCiAgICAgICAgICAgICAgIEVudHJvcHkgPSBlbnRyb3B5LA0KICAgICAgICAgICAgICAgU3BlY2llcyA9IHNwZWNpZXMsDQogICAgICAgICAgICAgICBFbnZpcm9ubWVudCA9IGkpDQogIH0sDQogIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSwNCiAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCg0KRGl2ZXJzaXR5IDwtIGRwbHlyOjpiaW5kX3Jvd3MoRGl2ZXJzaXR5KQ0KRGl2ZXJzaXR5IDwtIERpdmVyc2l0eSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgRXZlbm5lc3MgPSBFbnRyb3B5IC8gbG9nKFJpY2huZXNzKQ0KKQ0KYGBgDQoNCg0KIyMjIyMgUmljaG5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBSaWNobmVzcyA9IG1lYW4oUmljaG5lc3MpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gUmljaG5lc3MNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANClNvIHJpY2huZXNzIGhvdmVycyBhcm91bmQgYSBzaW1pbGFyIHJlZ2ltZSB0aHJvdWdob3V0IHRoZSBtYWpvcml0eSBvZiB0aGUgc2ltdWxhdGlvbi4NCk5vdGUgaW4gdGhpcyBwbG90IHRoYXQgd2UgaGF2ZSBlbXBoYXNpc2VkIG9uZSBlbnZpcm9ubWVudGFsIGN1cnZlIGFuZCBzdXBlcmltcG9zZWQgdGhlIG1lYW4gaW4gYmxhY2suDQpXZSBkbyBtYW5hZ2UgdG8gcmVhY2ggaGVpZ2h0cyBvZiAxMSBzcGVjaWVzIGluIG9uZSBlbnZpcm9ubWVudCwgYnV0IHRoZXNlIGhlaWdodHMgYXJlIHNob3J0bGl2ZWQuDQpJbnN0ZWFkLCB3ZSBzZWVtIHRvIG9ic2VydmUgYSAodGltZSBhbmQgZW52aXJvbm1lbnQgYXZlcmFnZWQpIHZhbHVlOg0KYGBge3J9IA0KbWVhbihEaXZlcnNpdHkkUmljaG5lc3MpDQpgYGANCihJZiB3ZSBjb25zaWRlciB0aGUgZmlyc3QgMTAsMDAwIHRpbWUgdW5pdHMgYXMgYnVybi1pbiwgd2UgaW5zdGVhZCBzZWUgYSB2YWx1ZSBvZg0KYGBge3J9IA0KbWVhbihEaXZlcnNpdHkkUmljaG5lc3NbRGl2ZXJzaXR5JFRpbWUgPiAxMDAwMF0pDQpgYGANCg0KDQojIyMjIyBFbnRyb3B5DQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRGl2ZXJzaXR5LCANCiAgZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFbnRyb3B5LA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBFbnRyb3B5ID0gbWVhbihFbnRyb3B5KQ0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEVudHJvcHkNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCkVudHJvcHkgaGFzIGEgc2ltaWxhciwgYnV0IGhpZ2hseSBlcnJhdGljLCBiZWhhdmlvdXIuDQpJZiBvbmUgdHJpZXMgdG8gZm9sbG93IG9uZSBvZiB0aGUgZW50cm9weSBjdXJ2ZXMsIHRoZW4gb25lIHNlZXMgdGhhdCB0aGV5IGhhdmUNCmZhaXJseSBzdWJzdGFudGlhbCBwZXJpb2RzIG9mIGFsbW9zdCBzbW9vdGggYmVoYXZpb3VyIGZvbGxvd2VkIGJ5IHN1ZGRlbmx5DQp2ZXJ5IG5vaXN5IGJlaGF2aW91ciwgYW5kIG5vaXNlIHNlZW1zIHRvIGJlIHRoZSBkb21pbmFudCBtb2RlIGlmIG9uZSB0cmllcw0KdG8gZXhhbWluZSB0aGUgbG93IGFscGhhIGVudmlyb25tZW50IGluIHRoZSBiYWNrZ3JvdW5kLg0KVGhlcmUgYXJlIHNvbWUgZWFzeSB0byBtYWtlIHByZWRpY3Rpb25zLg0KRXh0aW5jdGlvbnMgcmVkdWNlIHRoZSBlbnRyb3B5IGluIHRoZSBzeXN0ZW0sIGFzIHlvdSBiZWNvbWUgbW9yZSBjZXJ0YWluIGFib3V0DQp3aGF0IHJlbWFpbnMuDQpBbmFsb2dvdXNseSwgYXJyaXZhbHMgaW5jcmVhc2UgdGhlIGVudHJvcHkuDQpXZSBjYW4gcHJvYmFibHkgYmV0dGVyIHNlZSB0aGUgcmVsYXRpb25zaGlwIGJleW9uZCB0aGVzZSBwcmluY2lwbGVzIGJ5IHBsb3R0aW5nDQplbnRyb3B5IGFnYWluc3QgcmljaG5lc3MgYW5kIGNvbm5lY3Rpbmcgb2JzZXJ2YXRpb25zIGJ5IGVudmlyb25tZW50IGFuZCB0aW1lLg0KDQojIyMjIyBFbnRyb3B5LVJpY2huZXNzDQpgYGB7cn0NCiMgZ2dwbG90Mjo6Z2dwbG90KA0KIyAgIERpdmVyc2l0eSAlPiUgZHBseXI6OmZpbHRlcihFbnZpcm9ubWVudCA8IDQpLCANCiMgICBnZ3Bsb3QyOjphZXMoDQojICAgICB4ID0gUmljaG5lc3MsDQojICAgICB5ID0gRW50cm9weSwNCiMgICAgIGdyb3VwID0gRW52aXJvbm1lbnQsDQojICAgICBjb2xvciA9IFRpbWUNCiMgICApDQojICkgKyBnZ3Bsb3QyOjpnZW9tX3BhdGgoDQojICkgKyBnZ3Bsb3QyOjpndWlkZXMoDQojICAgYWxwaGEgPSAibm9uZSINCiMgKQ0KDQpwbG90bHk6OnBsb3RfbHkoZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6OmZpbHRlcihFbnZpcm9ubWVudCA8IDIpLA0KICAgICAgICAgICAgICAgIHggPSB+UmljaG5lc3MsIHkgPSB+RW50cm9weSwgeiA9IH5UaW1lLCB0eXBlID0gInNjYXR0ZXIzZCIsDQogICAgICAgICAgICAgICAgbW9kZSA9ICJsaW5lcyIsIG9wYWNpdHkgPSAxLCBsaW5lID0gbGlzdChjb2xvciA9IH5UaW1lKSkNCmBgYA0KSXQgc2VlbXMgcXVpdGUgaGFyZCB0byB0ZWxsLCBidXQgdGhlcmUgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGFueSBwYXJ0aWN1bGFyIG9yaWVudGF0aW9uIChjbG9ja3dpc2UsIGNvdW50ZXItY2xvY2t3aXNlKSBvciBzaW1pbGFyIHBhdHRlcm4gaGVyZS4NCg0KIyMjIyMgRXZlbm5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEV2ZW5uZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBFdmVubmVzcyA9IG1lYW4oRXZlbm5lc3MpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRXZlbm5lc3MNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCkV2ZW5uZXNzIGhlbHBzIGhpZ2hsaWdodCB0aGF0IHRoaXMgaXMgYSBoaWdoIHZhcmlhbmNlIHByb2Nlc3MgYnV0IHdpdGggYSByZWxhdGl2ZWx5IGNvbnN0cmFpbmVkIG1lYW4uDQoNCiMjIyMjIEVudmlyb25tZW50IERpdmVyc2l0eQ0KV2UgY2FuLCBvZiBjb3Vyc2UsIGZsaXAgdGhlIGlkZWEgb24gaXRzIGhlYWQuDQpJbnN0ZWFkIG9mIGV4YW1pbmluZyB0aGUgZGl2ZXJzaXR5IG9mIHNwZWNpZXMgd2l0aGluIGVudmlyb25tZW50cywgd2UgY2FuDQpsb29rIGF0IHRoZSBkaXZlcnNpdHkgb2YgZW52aXJvbm1lbnRzIG9jY3VwaWVkIGJ5IHNwZWNpZXMuDQpTaW5jZSB2ZXJ5IGZldyBzcGVjaWVzIGVuZCB1cCBvY2N1cHlpbmcgZW52aXJvbm1lbnRzLCB3ZSBqdXN0IGxvb2sgYXQgcmljaG5lc3MuDQpVbmZvcnR1bmF0ZWx5LCB0aGlzIGlzIHF1aXRlIGEgbWVtb3J5IGV4aGF1c3RpdmUgdGFzay4NCg0KYGBge3J9DQpFbnZEaXZlcnNpdHkgPC0gbGFwcGx5KA0KICAgIDE6KChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzKSwNCiAgICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgICAgICB0aW1lIDwtIGFidW5kWywgMV0NCiAgICAgICAgZW52IDwtIGFidW5kWywgMSArIGkgKyBudW1TcGVjaWVzICogKDE6cmVzdWx0JE51bUVudmlyb25tZW50cyAtIDEpXQ0KICAgICAgICByaWNobmVzcyA8LSByb3dTdW1zKGVudiAhPSAwKQ0KICAgICAgICBhYnVuZFN1bSA8LSByb3dTdW1zKGVudikNCiAgICAgICAgZW52aXJvbm1lbnRzIDwtIGFwcGx5KA0KICAgICAgICAgICAgZW52LCBNQVJHSU4gPSAxLA0KICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICAgICAgICAgIHRvU3RyaW5nKHdoaWNoKHggPiAwKSkNCiAgICAgICAgICAgIH0NCiAgICAgICAgKQ0KICAgICAgICBkYXRhLmZyYW1lKFRpbWUgPSB0aW1lLCANCiAgICAgICAgICAgICAgICAgICBSaWNobmVzcyA9IHJpY2huZXNzLCANCiAgICAgICAgICAgICAgICAgICBBYnVuZGFuY2UgPSBhYnVuZFN1bSwNCiAgICAgICAgICAgICAgICAgICBTcGVjaWVzID0gaSwNCiAgICAgICAgICAgICAgICAgICBFbnZpcm9ubWVudHMgPSBlbnZpcm9ubWVudHMpDQogICAgfSwNCiAgICBhYnVuZCA9IHJlc3VsdCRBYnVuZGFuY2UsDQogICAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCkVudkRpdmVyc2l0eSA8LSBkcGx5cjo6YmluZF9yb3dzKEVudkRpdmVyc2l0eSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRW52RGl2ZXJzaXR5ICU+JSBkcGx5cjo6ZmlsdGVyKFJpY2huZXNzID4gMSksIA0KICBhZXMoeCA9IFRpbWUsIHkgPSBSaWNobmVzcywgY29sb3IgPSBTcGVjaWVzKQ0KKSArIGdlb21fcG9pbnQoDQogIGFscGhhID0gMC4wMSwgc2l6ZSA9IDMNCikgKyBndWlkZXMoDQogIGNvbG9yID0gIm5vbmUiDQopDQpgYGANCg0KT25lIGltbWVkaWF0ZWx5IGludGVyZXN0aW5nIHRyZW5kIGhlcmUgaXMgdGhhdCB2ZXJ5IGZldyBzcGVjaWVzIGFyZSBwcmVzZW50DQphY3Jvc3MgbW9yZSB0aGFuIDUgZW52aXJvbm1lbnRzIGF0IGEgZ2l2ZW4gdGltZS4NCkluZGVlZCwgb25seSANCmBgYHtyfSANCnVuaXF1ZShFbnZEaXZlcnNpdHkkU3BlY2llc1tFbnZEaXZlcnNpdHkkUmljaG5lc3MgPiA1XSkNCmBgYCANCmFyZSBldmVyIHByZXNlbnQgaW4gbW9yZSB0aGFuIDUgZW52aXJvbm1lbnRzIGF0IG9uY2UuDQpXZSBjYW4gYWxzbyBleGFtaW5lIGhvdyBsb25nIHRoZXNlIHBlcmlvZHMgb2NjdXIgZm9yIGJ5IHNwZWNpZXMgYnkgdGFidWxhdGlvbi4NCldlIGJsb2NrIHRpbWVzIHNvIHRoYXQgZW50cmllcyBhcmUgYWxsIG9mIHRoZSBzYW1lIHVuaXQgbGVuZ3RoLCBhbmQgcm91bmQgdGhlDQphdmVyYWdlIHJpY2huZXNzIGR1cmluZyB0aGUgZ2l2ZW4gdGltZSB1bml0Lg0KDQpgYGB7cn0NCndpdGgoRW52RGl2ZXJzaXR5ICU+JSBtdXRhdGUoDQogIFRpbWUgPSBmbG9vcihUaW1lKQ0KICApICU+JSBncm91cF9ieSgNCiAgICBUaW1lLCBTcGVjaWVzDQogICAgKSAlPiUgc3VtbWFyaXNlKA0KICAgICAgUmljaG5lc3MgPSByb3VuZChtZWFuKFJpY2huZXNzKSksDQogICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICApLA0KICAgICB0YWJsZShTcGVjaWVzLCBSaWNobmVzcykpDQpgYGANCiMjIyMgQmV0YSBEaXZlcnNpdHkgey50YWJzZXR9DQoNCiMjIyMjIFByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzDQooRm9yIHRoZSBkZXNrdG9wLCB0aGUgYW1vdW50IG9mIGRhdGEgd2UgZ2VuZXJhdGVkIGlzIHRvbyBtdWNoLCBzbyB3ZSBuZWVkIHRvDQpyZWR1Y2UgdGhlIGFtb3VudCBvZiBkYXRhIHdlIHVzZS4NClRvIGRvIHNvIHdlIGF2ZXJhZ2Ugb3ZlciB0aW1lIGJsb2NrcywgaGVyZSBvZiBsZW5ndGggMTAwLikNCmBgYHtyfQ0KQXZlcmFnZWRBYnVuZGFuY2UgPC0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQopICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIHRpbWUNCikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQopDQpgYGANCg0KV2UgY2FuIHBlcmZvcm0gYSBQQ0EgYW5kIHNlZSBpZiBhcmUgZGF0YSBjYW4gYmUgc3VtbWFyaXNlZCBieSBhIHNtYWxsIG51bWJlciBvZg0KZGltZW5zaW9ucy4NCkFzIHdlIHNoYWxsIHNlZSwgY29uc3RyYWluaW5nIHRvIHRoZSBmaXJzdCAyNSBwcmluY2lwYWwgY29tcG9uZW50cyBkb2VzIG5vdA0KaGFybSB0aGUgc3lzdGVtIG11Y2guDQpgYGB7cn0NClBDQSA8LSBwcmNvbXAoQXZlcmFnZWRBYnVuZGFuY2UgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwgDQogICAgICAgICAgICAgIGNlbnRlciA9IFRSVUUsIHNjYWxlLiA9IFRSVUUsIHJhbmsuID0gMjUpDQpgYGANCg0KVGhlcmUgaXMgbm90IGFjdHVhbGx5IGEgbG90IG9mIGRlcGVuZGVuY2Ugd2l0aGluIHRoZSBzeXN0ZW0gaXQgYXBwZWFycy4NCkNvbnNpZGVyIHRoZSBhbW91bnQgb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBmaXJzdCBzaXggcHJpbmNpcGFsIGNvbXBvbmVudHMNCihvcmRlcmVkLCBhcyBpcyB0cmFkaXRpb24sIGJ5IGFtb3VudCBvZiB2YXJpYXRpb24gZXhwbGFpbmVkKS4NCg0KYGBge3J9DQpoZWFkKHN1bW1hcnkoUENBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGANCg0KVGhlIGFtb3VudCBvZiBleHBsYWluZWQgdmFyaWF0aW9uIGlzIGFzIGZvbGxvd3MuDQoNCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQoNClRoZSB0cmFkaXRpb25hbCBiaXBsb3QgZm9sbG93cywgYnV0IGRlc3BpdGUgdGhlIHNlZW1pbmcgcHJlc2VuY2Ugb2YgcGF0dGVybnMsDQpzbyBsaXR0bGUgb2YgdGhlIHZhcmlhbmNlIGlzIGV4cGxhaW5lZCB0aGF0IGlzIHByb2JhYmx5IG5vdCB3b3J0aCBmdXJ0aGVyDQpleGFtaW5hdGlvbi4NCg0KYGBge3J9DQpnZ3Bsb3QyOjphdXRvcGxvdChQQ0EsIGxvYWRpbmdzID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICBkYXRhID0gQXZlcmFnZWRBYnVuZGFuY2UgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwNCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ0aW1lIikNCmBgYA0KDQpXZSBjYW4gdHJ5IGFnYWluIHdpdGggcHJlc2VuY2UtYWJzZW5jZSBkYXRhIGluc3RlYWQuDQpgYGB7cn0NCkF2ZXJhZ2VkUEEgPC0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICBkcGx5cjo6YWNyb3NzKC5jb2xzID0gIXRpbWUsIC5mbnMgPSB+IC54ID4gMCkNCikgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgdGltZQ0KKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgZHBseXI6OmFjcm9zcyguZm5zID0gfiBtZWFuKC54KSkNCikNCmBgYA0KDQpgYGB7cn0NClBDQVBBIDwtIHByY29tcChBdmVyYWdlZFBBICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksIA0KICAgICAgICAgICAgICBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFLCByYW5rLiA9IDI1KQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChzdW1tYXJ5KFBDQVBBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGANClNvIGl0IGlzIGEgYml0IGJldHRlciwgYnV0IG5vdCBieSBtdWNoLg0KDQpgYGB7cn0NCmdncGxvdDI6OmF1dG9wbG90KFBDQVBBLCBsb2FkaW5ncyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IEF2ZXJhZ2VkUEEgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwNCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ0aW1lIikNCmBgYA0KDQpJIHNob3VsZCBub3RlIHRoYXQgdGhpcyBpcyBub3QgYSBmYWlsdXJlIG9mIHRoZSBtZXRob2QgcGVyc2F5LiANCldoYXQgdGhpcyBzYXlzIHRvIG1lIGlzIHRoYXQgZGltZW5zaW9uIHJlZHVjdGlvbiBvZiB0aGUgc3lzdGVtIGNhbm5vdCByZWR1Y2UgdGhlDQpzeXN0ZW0gdG8gYSBodW1hbi1yZWFkYWJsZSBzZXQgb2YgZGVzY3JpcHRvcnMuDQpUaGUgc3lzdGVtIGlzIHN0aWxsIGJlaW5nIHJlZHVjZWQgKGZyb20gMTAwIFNwZWNpZXMgKiAxMCBFbnZpcm9ubWVudHMgdG8gMjUgQ29tcG9uZW50cyB0byBjb3ZlciANCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgIA0Kb2YgdGhlIHZhcmlhdGlvbikuDQo8IS0tIA0KSW5zdGVhZCwgZ2l2ZW4gdGhlIG51bWJlciBvZiBlbnZpcm9ubWVudHMgYW5kIHRoZWlyIGluZGVwZW5kZW5jZSwgdGhpcyBzZWVtcyB0byANCnN1Z2dlc3QgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdGhlIGNoYW5nZSBpbiBlbnZpcm9ubWVudHMgY2FuIGJlIGRlc2NyaWJlZCBieSBhYm91dCB0d28gY29tcG9uZW50cyBwbHVzIHRpbWUgYW5kIHNvbWUgbWlub3IgdmFyaWF0aW9uIHdoZW4gY29uc2lkZXJpbmcgdGhlIHN5c3RlbSBhcyBhIHdob2xlLiANCihUaGlzIGRvZXMgbm90IHdvcmsgZm9yIHRoZSBpbmRpdmlkdWFsIHN5c3RlbXMgc2luY2UgdGhlcmUgaXMgcmVkdW5kYW5jeSB0aGF0IGlzIGxvc3QuKSAtLT4NCk15IGluaXRpYWwgaHlwb3RoZXNpcyBmb3Igd2hlbiB0aGUgc3lzdGVtcyBhcmUgY291cGxlZCBpcyB0aGF0LCBhcyBjb3VwbGluZyBpbmNyZWFzZXMsIHRoZSBudW1iZXIgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMgc2hvdWxkIGRlY3JlYXNlLCBzaW5jZSBvbmUgc3lzdGVtJ3MgY2hhbmdlcyB3aWxsIGJlIGJldHRlciBwcmVkaWN0b3JzIG9mIHRoZSBuZXh0IHN5c3RlbSdzIGNoYW5nZXMuDQooQW4gaW50ZXJlc3RpbmcgcmVsYXRlZCBxdWVzdGlvbjsgZG8gdGhlIG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cyBjb3JyZWxhdGUgd2l0aCB0aGUgYW1vdW50IG9mIGJpb2RpdmVyc2l0eSBpbiB0aGUgc3lzdGVtPw0KDQpgYGB7ciBjbGVhbnVwfQ0KaWZybShBdmVyYWdlZEFidW5kYW5jZSkNCmlmcm0oQXZlcmFnZWRQQSkNCmlmcm0oUENBKQ0KaWZybShQQ0FQQSkNCmBgYA0KDQojIyMjIyBCZXRhIERpdmVyc2l0eSBPdmVyIFRpbWUNClNpbmNlIHRyeWluZyB0byByZWR1Y2UgdGhlIHN5c3RlbSBkaW1lbnNpb25hbGx5IGRvZXMgbm90IHdvcmsgKHdlbGwgZW5vdWdoKSBhDQpuZXh0IGF0dGVtcHQgbWlnaHQgYmUgdG8gY29uc2lkZXIgaG93IHRoZSBwYWlyLXdpc2UgYmV0YSBkaXZlcnNpdHkgY2hhbmdlcyBvdmVyDQp0aGUgY291cnNlIG9mIHRoZSBzaW11bGF0aW9uLg0KSW5pdGlhbCBwcm9ibGVtcyBpbmNsdWRlIHRoYXQgdGhlcmUgYXJlIHF1aXRlIGEgZmV3IG1lYXN1cmVzIG9mIGJldGEgZGl2ZXJzaXR5IA0KYXMgd2VsbCBhcyB0aGF0IHRoZSAoc3F1YXJlIG9mIHRoZSkgbnVtYmVyIG9mIGVudmlyb25tZW50cyB3aWxsIGRldGVybWluZSBob3cNCm1hbnkgZW50cmllcyB3ZSBuZWVkIHRvIGNvbnNpZGVyLg0KDQpUbyB0cnkgdGhpcywgd2UgYXJlIGdvaW5nIHRvIHJlb3JnYW5pc2Ugb3VyIGRhdGEgaW50byBkYXRhIGZyYW1lIHdpdGggYXR0cmlidXRlcw0Kb2YgZW52aXJvbm1lbnQsIHRpbWUsIGFuZCB0cmFpdHMvc3BlY2llcy4NCmBgYHtyfQ0KRW52aXJvbm1lbnRzIDwtIGxhcHBseSgNCiAgMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzLA0KICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgIGVudiA8LSBhYnVuZFssIDEgKyAxOm51bVNwZWNpZXMgKyBudW1TcGVjaWVzICogKGkgLSAxKV0NCiAgICANCiAgICByZXR2YWwgPC0gZGF0YS5mcmFtZShFbnZpcm9ubWVudCA9IHRvU3RyaW5nKGkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWUgPSB0aW1lDQogICAgKQ0KICAgIA0KICAgIHJldHZhbCA8LSBjYmluZChyZXR2YWwsIGVudikNCiAgICBjb2xuYW1lcyhyZXR2YWwpIDwtIGMoIkVudmlyb25tZW50IiwgIlRpbWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJCYXNhbCIsIDE6MzQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJDb25zdW1lciIsIDM1OjEwMCkNCiAgICApDQogICAgcmV0dXJuKHJldHZhbCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIH0sDQogIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCiAgKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgICB0aW1lID0gZmxvb3IodGltZS8xMDApKjEwMA0KICApICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgdGltZQ0KICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQogICksDQogIG51bVNwZWNpZXMgPSAobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cw0KKQ0KDQpFbnZpcm9ubWVudHMgPC0gZHBseXI6OmJpbmRfcm93cyhFbnZpcm9ubWVudHMpDQpFbnZpcm9ubWVudHMgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6ZmlsdGVyKA0KICBkcGx5cjo6aWZfYW55KGRwbHlyOjpzdGFydHNfd2l0aChjKCJCYXNhbCIsICJDb25zdW1lciIpKSwgfiAoLnggIT0gMCkpLA0KKQ0KYGBgDQoNCiMjIyMjIENsdXN0ZXIgQW5hbHlzaXMgT3ZlciBUaW1lDQpPbmUgcGVyaGFwcyBpbnRlcmVzdGluZyBpZGVhIGlzIHRvIHRyeSB0byBncm91cCB0aGUgZW52aXJvbm1lbnRzIGJ5IGNsdXN0ZXINCmJ5IGNvbnNpZGVyaW5nIHRoZSBhYnVuZGFuY2VzIChvciBwcmVzZW5jZS1hYnNlbmNlKSBvZiB0aGUgc3BlY2llcyBwcmVzZW50IGFzIHRyYWl0cy4NClRoZSBxdWVzdGlvbiB0aGVuIGlzIHdoZXRoZXIgdGhlIGVudmlyb25tZW50cyBoYXZlIGEgdGVuZGVuY3kgdG8gYXR0cmFjdCB0byANCnNwZWNpZmljIHBvaW50cyBvciBpZiB0aGV5IHdhbmRlciBhcm91bmQgZWFjaCBvdGhlciB3aXRob3V0IHJlbGF0aW9uLg0KKFRoZSBsYXR0ZXIgaXMgbW9yZSBuZXV0cmFsLikNCklmIHRoZXkgYXBwZWFyIHRvIGJlIGNvbnZlcmdlbnQgKHdoaWNoIHNvIGZhciB3b3VsZCBzZWVtIHRvIGRpc2FncmVlIG1vc3RseSB3aXRoDQp0aGUgYWxwaGEgZGl2ZXJzaXR5IGFuYWx5c2VzKSwgdGhlbiB0aGF0IHdvdWxkIGltcGx5IHRoYXQgZHluYW1pY3MgZGV0ZXJtaW5lIHRoZQ0KbWFqb3JpdHkgb2YgdGhlIHN5c3RlbSdzIGZhdGUsIHdoaWxlIGlmIHRoZXkgaW5zdGVhZCB3YW5kZXIgbW9yZSByYW5kb21seSwgdGhlbg0KdGhleSB3b3VsZCBhcHBlYXIgdG8gYmUgZG9taW5hdGVkIGJ5IHRoZSBuZXV0cmFsIG1lY2hhbmlzbXMuDQooT2YgY291cnNlLCB0aGlzIGlzIGFmZmVjdGVkIGJ5IHRoZSByYXRlIG9mIHRoZSBuZXV0cmFsIG1lY2hhbmlzbXMsIHNvIGl0IGV4aXN0cw0Kb24gYSBkZWZpbml0ZSBjb250aW51dW0uKQ0KKE5vdGUsIG9mIGNvdXJzZSwgdGhhdCBjbHVzdGVyIGFuYWx5c2lzIHVzdWFsbHkgaW5jbHVkZXMgc29tZXRoaW5nIGxpa2UgUENBLA0Kc28gd2Ugd291bGQgbm90IG5lY2Vzc2FyaWx5IGV4cGVjdCBhIGRpZmZlcmVudCByZXN1bHQuKQ0KDQpNeSB3b3JraW5nIGh5cG90aGVzaXMgZm9yIGhvdyB0byB1c2UgdGhlIGRpc3RhbmNlIG1lYXN1cmVzIGlzIHRoYXQgd2UgbmVlZCBhDQptZXRyaWMgbWVhc3VyZSAoS2luZHQgYW5kIENvZSwgMjAwNSkgd2l0aCBhbnkgaWRlbnRpZmljYXRpb24gc2NydWJiZWQgb2ZmIChzaXRlLA0KdGltZSBjb2xsZWN0ZWQpLg0KS2luZHQgYW5kIENvZSBzdWdnZXN0IHRoYXQgdGFraW5nIHRoZSBzcXVhcmUgcm9vdCBvZiBCcmF5LUN1cnRpcyBtYWtlcyBpdCBtZXRyaWMNCmFuZCB0aHVzIHVzYWJsZSBmb3IgbWF0aGVtYXRpY2FsIGRpc3RhbmNlIGFuYWx5c2VzIChzdWNoIGFzIGhpZXJhcmNoaWNhbCANCmNsdXN0ZXJpbmcpLg0KVGhlIGB2ZWdhbmAgcGFja2FnZSByZWNvbWVuZHMgdXNpbmcgSmFjY2FyZCBpbnN0ZWFkIG9mIEJyYXktQ3VydGlzLCBmb3IgdGhlIHNhbWUNCnJlYXNvbi4NCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlIDwtIEVudmlyb25tZW50cyAlPiUgZHBseXI6OnNlbGVjdCgNCiAgLUVudmlyb25tZW50LCAtVGltZQ0KKSAlPiUgdmVnYW46OnZlZ2Rpc3QobWV0aG9kID0gImphY2NhcmQiKQ0KYGBgDQoNCmBgYHtyfQ0KRW52RGlzdENsdXN0IDwtIGhjbHVzdChFbnZpcm9ubWVudERpc3RhbmNlKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3QsIGxhYmVscyA9IEZBTFNFKQ0KYGBgDQpOb3RlIHRoYXQgYXJlIG1ham9yIGJyZWFrZG93bnMgZXh0cmVtZWx5IGVhcmx5IHlpZWxkaW5nIGEgbGFyZ2UgbnVtYmVyIG9mIA0KY2x1c3RlcnMgYnV0IHRoZXJlIGFyZSBleHBlY3RlZCB0byBiZSAxMCBlbnZpcm9ubWVudHMgKGlmIGhpc3RvcmljYWwgY29udGluZ2VuY3kgDQptYXR0ZXJlZCBtb3N0KSBvciBhIHNtYWxsIG51bWJlciBvZiBjbHVzdGVycyAoaWYgdGhleSBhcmUgY29udmVyZ2luZykuDQpUaGUgbGFyZ2UgbnVtYmVyIG9mIGNsdXN0ZXJzIHNheXMgdG8gbWUgdGhhdCB0aGUgbmV1dHJhbCBkeW5hbWljcyBhcmUganVtcGluZw0KcmFwaWRseSBmcm9tIGNsdXN0ZXIgdG8gY2x1c3Rlci4NCg0KV2UgY2FuIHRyeSBhZ2FpbiB3aXRoIHByZXNlbmNlIGFic2VuY2UgaW5zdGVhZC4NCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlUEEgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6c2VsZWN0KA0KICAtRW52aXJvbm1lbnQsIC1UaW1lDQopICU+JSB2ZWdhbjo6dmVnZGlzdChtZXRob2QgPSAiamFjY2FyZCIsIGJpbmFyeSA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpFbnZEaXN0Q2x1c3RQQSA8LSBoY2x1c3QoRW52aXJvbm1lbnREaXN0YW5jZVBBKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3RQQSwgbGFiZWxzID0gRkFMU0UpDQpgYGANCg0KPCEtLSMjIyMjIFN0YXRlIE5ldHdvcmsNCkFuIGFkZGl0aW9uYWwgaWRlYSB0aGF0IG1pZ2h0IGJlIHdvcnRoIGFuIGluaXRpYWwgbG9vayBvdmVyIGlzIHdoZXRoZXIgd2UgY2FuDQpjcmVhdGUgdGhlIHN1YnNwYWNlIG9mIHRoZSBncmFwaCBleHBsb3JlZCBieSB0aGUgc2ltdWxhdGlvbiBzZXQuIA0KVGhpcyBpcyBwcm9iYWJseSBvZiB0aGUgbG93ZXN0IHByaW9yaXR5IG9mIHRoZSB0aGluZ3MgZGVzY3JpYmVkIGFib3ZlLCBidXQgdGhpcw0KaXMgb2YgbWF0aGVtYXRpY2FsIGludGVyZXN0IGFuZCBkb2VzIGhhdmUgc29tZSByZWxhdGlvbnNoaXAgdG8gZGl2ZXJzaXR5IA0KbWVhc3VyZXMuDQpTdWNoIGEgbWFwIHdvdWxkIHVzdWFsbHkgYmUgdmlzdWFsaXNlZCB3aXRoIGNvbHVtbnMgcmVwcmVzZW50aW5nIHJpY2huZXNzIGFuZA0KZWRnZXMgcmVwcmVzZW50aW5nIG1vdmVzIGFsb25nIHRoZSBzeXN0ZW0uDQpXZSBjYW4gY29sb3VyIHRoZSBlZGdlcyB0byByZXByZXNlbnQgd2hldGhlciB0aGV5IHdlcmUgdmlhIGFycml2YWwsIGR5bmFtaWMNCmV4dGluY3Rpb24sIG9yIG5ldXRyYWwgZXh0aW5jdGlvbiBhcyB3ZWxsLiAtLT4NCg0KIyMgUmVzdWx0cywgTGluZSBTcGF0aWFsIFN0cnVjdHVyZSB7LnRhYnNldH0NCmBgYHtyfQ0KaWZybShEaXZlcnNpdHkpDQppZnJtKEVudkRpc3RDbHVzdCkNCmlmcm0oRW52RGlzdENsdXN0UEEpDQppZnJtKEVudkRpdmVyc2l0eSkNCmlmcm0oRW52aXJvbm1lbnRzKQ0KaWZybShFbnZpcm9ubWVudERpc3RhbmNlKQ0KaWZybShFbnZpcm9ubWVudERpc3RhbmNlUEEpDQpgYGANCg0KV2UgcmVwZWF0IHRoZSBwcm9jZWR1cmVzIGFib3ZlIGZvciB0aGUgbGluZWFyIHNldC4NCldlIHVzZSB0aGUgc2FtZSBwb29sIGFuZCBpbnRlcmFjdGlvbiBtYXRyaWNlcywgYnV0IGNoYW5nZSB0aGUgc3BhY2Ugc28gdGhhdA0KYGBhZGphY2VudCcnIGNvbW11bml0aWVzIGNhbiBkaXNwZXJzZSAoZS5nLiAxIDwtPiAyIDwtPiAzIDwtPi4uLjwtPiA5IDwtPiAxMCkuDQpTaW1pbGFybHksIHRoZSBoaXN0b3J5IGlzIHRoZSBzYW1lIGluIHRlcm1zIG9mIGFycml2YWxzIGFuZCBleHRpbmN0aW9ucy4NCg0KYGBge3J9DQpsb2FkKGZpbGUucGF0aCgNCiAgIi4uIiwgImV4cGVyaW1lbnRzIiwgIk1OQS1GaXJzdEF0dGVtcHQtUmVzdWx0LUVudjEwLUxpbmUuUkRhdGEiKQ0KKQ0KdG9SZW1vdmUgPC0gcmVzdWx0JEFidW5kYW5jZSA8PSByZXN1bHQkUGFyYW1ldGVycyRFbGltaW5hdGlvblRocmVzaG9sZA0KcmVzdWx0JEFidW5kYW5jZVt0b1JlbW92ZV0gPC0gMA0KaWZybSh0b1JlbW92ZSkNCmBgYA0KDQojIyMgQWJ1bmRhbmNlIHsudGFic2V0fQ0KDQpXaXRoIDEwIGVudmlyb25tZW50cywgaXQgaXMgcHJvYmFibHkgbm90IGhlbHBmdWwgdG8gY2hlY2sgMTAgaW5kaXZpZHVhbCBhYnVuZGFuY2UgY3VydmVzLCBidXQgbG9va2luZyBhdCB0aGUgZmlyc3Qgb25lIG1pZ2h0IGJlIGhlbHBmdWwuDQpgYGB7cn0NCkxhd01vcnRvbjE5OTZfUGxvdEFidW5kYW5jZShyZXN1bHQkQWJ1bmRhbmNlW3NlcShmcm9tID0gMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBucm93KHJlc3VsdCRBYnVuZGFuY2UpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IDEwKSwgYygxLCAyOjEwMSldKSAtPiBvYmo7DQpgYGANCmBgYHtyfQ0Kb2JqICsgZ2dwbG90Mjo6c2NhbGVfeV9sb2cxMCgpICsgZ2dwbG90Mjo6Z3VpZGVzKGNvbG9yID0gRkFMU0UpDQpgYGANCkV2ZXJ5IHZlcnRpY2FsIGxpbmUgaXMgYSBzcGVjaWVzIGludHJvZHVjdGlvbiBvciBleHRpbmN0aW9uIGJ5IG5ldXRyYWwgZHluYW1pY3MuDQpOb3RlIHRoYXQsIGR1ZSB0byBkaXNwZXJzYWwsIHRoZXNlIHNob3VsZCBsb3NlIHNvbWUgb2YgdGhlaXIgYWJ1bmRhbmNlIHRvIA0Kc3ByZWFkaW5nLCBidXQgaWYgdGhhdCBzcHJlYWRpbmcgaXMgZmFzdGVyIHRoYW4gdGhlIHBvcHVsYXRpb24gY2FuIHJlY291cCBpdHMNCmxvc3NlcywgdGhhbiBpdCBjYW4gaW52YWRlIGFuZCBiZSB3aXBlZCBvdXQgKHNpbmNlIGludmFkYWJpbGl0eSBkb2VzIG5vdCANCmNvbnNpZGVyIHNwYXRpYWwgZHluYW1pY3MsIG9ubHkgbG9jYWwgZWNvbG9naWNhbCBkeW5hbWljcykuDQpUaGUgcHJpbWFyeSBub3RlcyB0aGVuIGFyZSB0aGF0IHRoZXJlIGFyZSBhIGxhcmdlIG51bWJlciBtb3JlIGV2ZW50cyBhcHBlYXJpbmcNCnRvIG9jY3VyIChkdWUgdG8gdGhlIHNwYXRpYWwgYXJyYW5nZW1lbnQpLCBidXQgdGhlIHN5c3RlbSBhcyBhIHdob2xlIGlzIHN0YWJsZXINCihzaW5jZSB0aGUgc3BlY2llcyB0aGF0IGdldCBrbm9ja2VkIG91dCBjYW4gYmUgcmVjYXB0dXJlZCBmcm9tIHNwYWNlIGFzIHdlbGwpLg0KDQojIyMjIEFscGhhIERpdmVyc2l0eSBwbG90cyB7LnRhYnNldH0NCg0KIyMjIyMgSW50cm8NCmBgYHtyfQ0KRGl2ZXJzaXR5IDwtIGxhcHBseSgNCiAgMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzLA0KICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgIGVudiA8LSBhYnVuZFssIDEgKyAxOm51bVNwZWNpZXMgKyBudW1TcGVjaWVzICogKGkgLSAxKV0NCiAgICByaWNobmVzcyA8LSByb3dTdW1zKGVudiAhPSAwKQ0KICAgIGFidW5kU3VtIDwtIHJvd1N1bXMoZW52KQ0KICAgIGVudHJvcHkgPC0gZW52IC8gYWJ1bmRTdW0NCiAgICBlbnRyb3B5IDwtIC0gYXBwbHkoDQogICAgICBlbnRyb3B5LCBNQVJHSU4gPSAxLA0KICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICBzdW0oaWZlbHNlKHggIT0gMCwgeCAqIGxvZyh4KSwgMCkpDQogICAgICB9KQ0KICAgIHNwZWNpZXMgPC0gYXBwbHkoDQogICAgICBlbnYsIE1BUkdJTiA9IDEsDQogICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgIHRvU3RyaW5nKHdoaWNoKHggPiAwKSkNCiAgICAgIH0NCiAgICApDQogICAgZGF0YS5mcmFtZShUaW1lID0gdGltZSwgDQogICAgICAgICAgICAgICBSaWNobmVzcyA9IHJpY2huZXNzLCANCiAgICAgICAgICAgICAgIEVudHJvcHkgPSBlbnRyb3B5LA0KICAgICAgICAgICAgICAgU3BlY2llcyA9IHNwZWNpZXMsDQogICAgICAgICAgICAgICBFbnZpcm9ubWVudCA9IGkpDQogIH0sDQogIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSwNCiAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCg0KRGl2ZXJzaXR5IDwtIGRwbHlyOjpiaW5kX3Jvd3MoRGl2ZXJzaXR5KQ0KRGl2ZXJzaXR5IDwtIERpdmVyc2l0eSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgRXZlbm5lc3MgPSBFbnRyb3B5IC8gbG9nKFJpY2huZXNzKQ0KKQ0KYGBgDQoNCg0KIyMjIyMgUmljaG5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBSaWNobmVzcyA9IG1lYW4oUmljaG5lc3MpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gUmljaG5lc3MNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCg0KIyMjIyMgRW50cm9weQ0KYGBge3J9DQpnZ3Bsb3QyOjpnZ3Bsb3QoDQogIERpdmVyc2l0eSwgDQogIGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRW50cm9weSwNCiAgICBjb2xvciA9IGZhY3RvcihFbnZpcm9ubWVudCksDQogICAgYWxwaGEgPSBpZmVsc2UoRW52aXJvbm1lbnQgPT0gMSwgMSwgMC4zKQ0KICApDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCiAgZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIFRpbWUNCiAgICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgICAgRW50cm9weSA9IG1lYW4oRW50cm9weSkNCiAgICApLA0KICBtYXBwaW5nID0gZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFbnRyb3B5DQogICksDQogIGNvbG9yID0gImJsYWNrIiwNCiAgaW5oZXJpdC5hZXMgPSBGQUxTRQ0KKSArIGdncGxvdDI6Omd1aWRlcygNCiAgYWxwaGEgPSAibm9uZSINCikgKyBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9kaXNjcmV0ZSgNCiAgIkVudmlyb25tZW50Ig0KKQ0KYGBgDQoNCiMjIyMjIEVudHJvcHktUmljaG5lc3MNCmBgYHtyfQ0KcGxvdGx5OjpwbG90X2x5KGRhdGEgPSBEaXZlcnNpdHkgJT4lIGRwbHlyOjpmaWx0ZXIoRW52aXJvbm1lbnQgPCAyKSwNCiAgICAgICAgICAgICAgICB4ID0gflJpY2huZXNzLCB5ID0gfkVudHJvcHksIHogPSB+VGltZSwgdHlwZSA9ICJzY2F0dGVyM2QiLA0KICAgICAgICAgICAgICAgIG1vZGUgPSAibGluZXMiLCBvcGFjaXR5ID0gMSwgbGluZSA9IGxpc3QoY29sb3IgPSB+VGltZSkpDQpgYGANCg0KIyMjIyMgRXZlbm5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEV2ZW5uZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBFdmVubmVzcyA9IG1lYW4oRXZlbm5lc3MpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRXZlbm5lc3MNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCg0KIyMjIyMgRW52aXJvbm1lbnQgRGl2ZXJzaXR5DQoNCmBgYHtyfQ0KRW52RGl2ZXJzaXR5IDwtIGxhcHBseSgNCiAgICAxOigobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cyksDQogICAgZnVuY3Rpb24oaSwgYWJ1bmQsIG51bVNwZWNpZXMpIHsNCiAgICAgICAgdGltZSA8LSBhYnVuZFssIDFdDQogICAgICAgIGVudiA8LSBhYnVuZFssIDEgKyBpICsgbnVtU3BlY2llcyAqICgxOnJlc3VsdCROdW1FbnZpcm9ubWVudHMgLSAxKV0NCiAgICAgICAgcmljaG5lc3MgPC0gcm93U3VtcyhlbnYgIT0gMCkNCiAgICAgICAgYWJ1bmRTdW0gPC0gcm93U3VtcyhlbnYpDQogICAgICAgIGVudmlyb25tZW50cyA8LSBhcHBseSgNCiAgICAgICAgICAgIGVudiwgTUFSR0lOID0gMSwNCiAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgICAgICAgICB0b1N0cmluZyh3aGljaCh4ID4gMCkpDQogICAgICAgICAgICB9DQogICAgICAgICkNCiAgICAgICAgZGF0YS5mcmFtZShUaW1lID0gdGltZSwgDQogICAgICAgICAgICAgICAgICAgUmljaG5lc3MgPSByaWNobmVzcywgDQogICAgICAgICAgICAgICAgICAgQWJ1bmRhbmNlID0gYWJ1bmRTdW0sDQogICAgICAgICAgICAgICAgICAgU3BlY2llcyA9IGksDQogICAgICAgICAgICAgICAgICAgRW52aXJvbm1lbnRzID0gZW52aXJvbm1lbnRzKQ0KICAgIH0sDQogICAgYWJ1bmQgPSByZXN1bHQkQWJ1bmRhbmNlLA0KICAgIG51bVNwZWNpZXMgPSAobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cw0KKQ0KDQpFbnZEaXZlcnNpdHkgPC0gZHBseXI6OmJpbmRfcm93cyhFbnZEaXZlcnNpdHkpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QyOjpnZ3Bsb3QoDQogIEVudkRpdmVyc2l0eSAlPiUgZHBseXI6OmZpbHRlcihSaWNobmVzcyA+IDEpLCANCiAgYWVzKHggPSBUaW1lLCB5ID0gUmljaG5lc3MsIGNvbG9yID0gU3BlY2llcykNCikgKyBnZW9tX3BvaW50KA0KICBhbHBoYSA9IDAuMDEsIHNpemUgPSAzDQopICsgZ3VpZGVzKA0KICBjb2xvciA9ICJub25lIg0KKQ0KYGBgDQoNCmBgYHtyfQ0Kd2l0aChFbnZEaXZlcnNpdHkgJT4lIG11dGF0ZSgNCiAgVGltZSA9IGZsb29yKFRpbWUpDQogICkgJT4lIGdyb3VwX2J5KA0KICAgIFRpbWUsIFNwZWNpZXMNCiAgICApICU+JSBzdW1tYXJpc2UoDQogICAgICBSaWNobmVzcyA9IHJvdW5kKG1lYW4oUmljaG5lc3MpKSwNCiAgICAgIC5ncm91cHMgPSAiZHJvcCINCiAgICAgICksDQogICAgIHRhYmxlKFNwZWNpZXMsIFJpY2huZXNzKSkNCmBgYA0KIyMjIyBCZXRhIERpdmVyc2l0eSB7LnRhYnNldH0NCg0KIyMjIyMgUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMNCmBgYHtyfQ0KQXZlcmFnZWRBYnVuZGFuY2UgPC0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQopICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIHRpbWUNCikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQopDQpgYGANCg0KYGBge3J9DQpQQ0EgPC0gcHJjb21wKEF2ZXJhZ2VkQWJ1bmRhbmNlICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksIA0KICAgICAgICAgICAgICBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFLCByYW5rLiA9IDI1KQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQoNCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90Mjo6YXV0b3Bsb3QoUENBLCBsb2FkaW5ncyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IEF2ZXJhZ2VkQWJ1bmRhbmNlICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksDQogICAgICAgICAgICAgICAgICBjb2xvdXIgPSAidGltZSIpDQpgYGANCg0KYGBge3J9DQpBdmVyYWdlZFBBIDwtIHJlc3VsdCRBYnVuZGFuY2UgJT4lIGRhdGEuZnJhbWUoDQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICB0aW1lID0gZmxvb3IodGltZS8xMDApKjEwMA0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgZHBseXI6OmFjcm9zcyguY29scyA9ICF0aW1lLCAuZm5zID0gfiAueCA+IDApDQopICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIHRpbWUNCikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQopDQpgYGANCg0KYGBge3J9DQpQQ0FQQSA8LSBwcmNvbXAoQXZlcmFnZWRQQSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLCANCiAgICAgICAgICAgICAgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSwgcmFuay4gPSAyNSkNCmBgYA0KDQpgYGB7cn0NCmhlYWQoc3VtbWFyeShQQ0FQQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90Mjo6YXV0b3Bsb3QoUENBUEEsIGxvYWRpbmdzID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICBkYXRhID0gQXZlcmFnZWRQQSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLA0KICAgICAgICAgICAgICAgICAgY29sb3VyID0gInRpbWUiKQ0KYGBgDQoNCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgIA0KDQpgYGB7ciBjbGVhbnVwMn0NCmlmcm0oQXZlcmFnZWRBYnVuZGFuY2UpDQppZnJtKEF2ZXJhZ2VkUEEpDQppZnJtKFBDQSkNCmlmcm0oUENBUEEpDQpgYGANCg0KIyMjIyMgQmV0YSBEaXZlcnNpdHkgT3ZlciBUaW1lDQpgYGB7cn0NCkVudmlyb25tZW50cyA8LSBsYXBwbHkoDQogIDE6cmVzdWx0JE51bUVudmlyb25tZW50cywNCiAgZnVuY3Rpb24oaSwgYWJ1bmQsIG51bVNwZWNpZXMpIHsNCiAgICB0aW1lIDwtIGFidW5kWywgMV0NCiAgICBlbnYgPC0gYWJ1bmRbLCAxICsgMTpudW1TcGVjaWVzICsgbnVtU3BlY2llcyAqIChpIC0gMSldDQogICAgDQogICAgcmV0dmFsIDwtIGRhdGEuZnJhbWUoRW52aXJvbm1lbnQgPSB0b1N0cmluZyhpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lID0gdGltZQ0KICAgICkNCiAgICANCiAgICByZXR2YWwgPC0gY2JpbmQocmV0dmFsLCBlbnYpDQogICAgY29sbmFtZXMocmV0dmFsKSA8LSBjKCJFbnZpcm9ubWVudCIsICJUaW1lIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiQmFzYWwiLCAxOjM0KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiQ29uc3VtZXIiLCAzNToxMDApDQogICAgKQ0KICAgIHJldHVybihyZXR2YWwpDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICB9LA0KICBhYnVuZCA9IHJlc3VsdCRBYnVuZGFuY2UgJT4lIGRhdGEuZnJhbWUoDQogICkgJT4lIGRwbHlyOjptdXRhdGUoDQogICAgdGltZSA9IGZsb29yKHRpbWUvMTAwKSoxMDANCiAgKSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIHRpbWUNCiAgKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgICBkcGx5cjo6YWNyb3NzKC5mbnMgPSB+IG1lYW4oLngpKQ0KICApLA0KICBudW1TcGVjaWVzID0gKG5jb2wocmVzdWx0JEFidW5kYW5jZSkgLSAxKSAvIHJlc3VsdCROdW1FbnZpcm9ubWVudHMNCikNCg0KRW52aXJvbm1lbnRzIDwtIGRwbHlyOjpiaW5kX3Jvd3MoRW52aXJvbm1lbnRzKQ0KRW52aXJvbm1lbnRzIDwtIEVudmlyb25tZW50cyAlPiUgZHBseXI6OmZpbHRlcigNCiAgZHBseXI6OmlmX2FueShkcGx5cjo6c3RhcnRzX3dpdGgoYygiQmFzYWwiLCAiQ29uc3VtZXIiKSksIH4gKC54ICE9IDApKSwNCikNCmBgYA0KDQojIyMjIyBDbHVzdGVyIEFuYWx5c2lzIE92ZXIgVGltZQ0KDQpgYGB7cn0NCkVudmlyb25tZW50RGlzdGFuY2UgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6c2VsZWN0KA0KICAtRW52aXJvbm1lbnQsIC1UaW1lDQopICU+JSB2ZWdhbjo6dmVnZGlzdChtZXRob2QgPSAiamFjY2FyZCIpDQpgYGANCg0KYGBge3J9DQpFbnZEaXN0Q2x1c3QgPC0gaGNsdXN0KEVudmlyb25tZW50RGlzdGFuY2UpDQpgYGANCg0KYGBge3J9DQpwbG90KEVudkRpc3RDbHVzdCwgbGFiZWxzID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlUEEgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6c2VsZWN0KA0KICAtRW52aXJvbm1lbnQsIC1UaW1lDQopICU+JSB2ZWdhbjo6dmVnZGlzdChtZXRob2QgPSAiamFjY2FyZCIsIGJpbmFyeSA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpFbnZEaXN0Q2x1c3RQQSA8LSBoY2x1c3QoRW52aXJvbm1lbnREaXN0YW5jZVBBKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3RQQSwgbGFiZWxzID0gRkFMU0UpDQpgYGANCiMjIFJlc3VsdHMsIFNsb3cgTGluZSBTcGF0aWFsIFN0cnVjdHVyZSB7LnRhYnNldH0NCmBgYHtyfQ0KaWZybShEaXZlcnNpdHkpDQppZnJtKEVudkRpc3RDbHVzdCkNCmlmcm0oRW52RGlzdENsdXN0UEEpDQppZnJtKEVudkRpdmVyc2l0eSkNCmlmcm0oRW52aXJvbm1lbnRzKQ0KaWZybShFbnZpcm9ubWVudERpc3RhbmNlKQ0KaWZybShFbnZpcm9ubWVudERpc3RhbmNlUEEpDQpgYGANCg0KV2UgcmVwZWF0IHRoZSBwcm9jZWR1cmVzIGFib3ZlIGZvciB0aGUgbGluZWFyIHNldC4NCldlIHVzZSB0aGUgc2FtZSBwb29sIGFuZCBpbnRlcmFjdGlvbiBtYXRyaWNlcywgYnV0IGNoYW5nZSB0aGUgc3BhY2Ugc28gdGhhdA0KYGBhZGphY2VudCcnIGNvbW11bml0aWVzIGNhbiBkaXNwZXJzZSAoZS5nLiAxIDwtPiAyIDwtPiAzIDwtPi4uLjwtPiA5IDwtPiAxMCkuDQpTaW1pbGFybHksIHRoZSBoaXN0b3J5IGlzIHRoZSBzYW1lIGluIHRlcm1zIG9mIGFycml2YWxzIGFuZCBleHRpbmN0aW9ucy4NCg0KYGBge3J9DQpsb2FkKGZpbGUucGF0aCgNCiAgIi4uIiwgImV4cGVyaW1lbnRzIiwgIk1OQS1GaXJzdEF0dGVtcHQxZSswNi1SZXN1bHQtRW52MTAtTGluZS5SRGF0YSIpDQopDQp0b1JlbW92ZSA8LSByZXN1bHQkQWJ1bmRhbmNlIDw9IHJlc3VsdCRQYXJhbWV0ZXJzJEVsaW1pbmF0aW9uVGhyZXNob2xkDQpyZXN1bHQkQWJ1bmRhbmNlW3RvUmVtb3ZlWzE6KGxlbmd0aCh0b1JlbW92ZSkvMildXSA8LSAwDQpyZXN1bHQkQWJ1bmRhbmNlW3RvUmVtb3ZlWyhsZW5ndGgodG9SZW1vdmUpLzIgKyAxKTpsZW5ndGgodG9SZW1vdmUpXV0gPC0gMA0KaWZybSh0b1JlbW92ZSkNCmBgYA0KDQojIyMgQWJ1bmRhbmNlIHsudGFic2V0fQ0KDQpXaXRoIDEwIGVudmlyb25tZW50cywgaXQgaXMgcHJvYmFibHkgbm90IGhlbHBmdWwgdG8gY2hlY2sgMTAgaW5kaXZpZHVhbCBhYnVuZGFuY2UgY3VydmVzLCBidXQgbG9va2luZyBhdCB0aGUgZmlyc3Qgb25lIG1pZ2h0IGJlIGhlbHBmdWwuDQpgYGB7cn0NCkxhd01vcnRvbjE5OTZfUGxvdEFidW5kYW5jZShyZXN1bHQkQWJ1bmRhbmNlW3NlcShmcm9tID0gMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBucm93KHJlc3VsdCRBYnVuZGFuY2UpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IDEwKSwgYygxLCAyOjEwMSldKSAtPiBvYmo7DQpgYGANCmBgYHtyfQ0Kb2JqICsgZ2dwbG90Mjo6c2NhbGVfeV9sb2cxMCgpICsgZ2dwbG90Mjo6Z3VpZGVzKGNvbG9yID0gRkFMU0UpDQpgYGANCg0KIyMjIyBBbHBoYSBEaXZlcnNpdHkgcGxvdHMgey50YWJzZXR9DQoNCiMjIyMjIEludHJvDQpgYGB7cn0NCkRpdmVyc2l0eSA8LSBsYXBwbHkoDQogIDE6cmVzdWx0JE51bUVudmlyb25tZW50cywNCiAgZnVuY3Rpb24oaSwgYWJ1bmQsIG51bVNwZWNpZXMpIHsNCiAgICB0aW1lIDwtIGFidW5kWywgMV0NCiAgICBlbnYgPC0gYWJ1bmRbLCAxICsgMTpudW1TcGVjaWVzICsgbnVtU3BlY2llcyAqIChpIC0gMSldDQogICAgcmljaG5lc3MgPC0gcm93U3VtcyhlbnYgIT0gMCkNCiAgICBhYnVuZFN1bSA8LSByb3dTdW1zKGVudikNCiAgICBlbnRyb3B5IDwtIGVudiAvIGFidW5kU3VtDQogICAgZW50cm9weSA8LSAtIGFwcGx5KA0KICAgICAgZW50cm9weSwgTUFSR0lOID0gMSwNCiAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgc3VtKGlmZWxzZSh4ICE9IDAsIHggKiBsb2coeCksIDApKQ0KICAgICAgfSkNCiAgICBzcGVjaWVzIDwtIGFwcGx5KA0KICAgICAgZW52LCBNQVJHSU4gPSAxLA0KICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICB0b1N0cmluZyh3aGljaCh4ID4gMCkpDQogICAgICB9DQogICAgKQ0KICAgIGRhdGEuZnJhbWUoVGltZSA9IHRpbWUsIA0KICAgICAgICAgICAgICAgUmljaG5lc3MgPSByaWNobmVzcywgDQogICAgICAgICAgICAgICBFbnRyb3B5ID0gZW50cm9weSwNCiAgICAgICAgICAgICAgIFNwZWNpZXMgPSBzcGVjaWVzLA0KICAgICAgICAgICAgICAgRW52aXJvbm1lbnQgPSBpKQ0KICB9LA0KICBhYnVuZCA9IHJlc3VsdCRBYnVuZGFuY2UsDQogIG51bVNwZWNpZXMgPSAobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cw0KKQ0KDQoNCkRpdmVyc2l0eSA8LSBkcGx5cjo6YmluZF9yb3dzKERpdmVyc2l0eSkNCkRpdmVyc2l0eSA8LSBEaXZlcnNpdHkgJT4lIGRwbHlyOjptdXRhdGUoDQogIEV2ZW5uZXNzID0gRW50cm9weSAvIGxvZyhSaWNobmVzcykNCikNCmBgYA0KDQoNCiMjIyMjIFJpY2huZXNzDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRGl2ZXJzaXR5LCANCiAgZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBSaWNobmVzcywNCiAgICBjb2xvciA9IGZhY3RvcihFbnZpcm9ubWVudCksDQogICAgYWxwaGEgPSBpZmVsc2UoRW52aXJvbm1lbnQgPT0gMSwgMSwgMC4zKQ0KICApDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCiAgZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIFRpbWUNCiAgICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgICAgUmljaG5lc3MgPSBtZWFuKFJpY2huZXNzKQ0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzDQogICksDQogIGNvbG9yID0gImJsYWNrIiwNCiAgaW5oZXJpdC5hZXMgPSBGQUxTRQ0KKSArIGdncGxvdDI6Omd1aWRlcygNCiAgYWxwaGEgPSAibm9uZSINCikgKyBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9kaXNjcmV0ZSgNCiAgIkVudmlyb25tZW50Ig0KKQ0KYGBgDQoNCiMjIyMjIEVudHJvcHkNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEVudHJvcHksDQogICAgY29sb3IgPSBmYWN0b3IoRW52aXJvbm1lbnQpLA0KICAgIGFscGhhID0gaWZlbHNlKEVudmlyb25tZW50ID09IDEsIDEsIDAuMykNCiAgKQ0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQogIGRhdGEgPSBEaXZlcnNpdHkgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgICBUaW1lDQogICAgKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgICAgIEVudHJvcHkgPSBtZWFuKEVudHJvcHkpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRW50cm9weQ0KICApLA0KICBjb2xvciA9ICJibGFjayIsDQogIGluaGVyaXQuYWVzID0gRkFMU0UNCikgKyBnZ3Bsb3QyOjpndWlkZXMoDQogIGFscGhhID0gIm5vbmUiDQopICsgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZGlzY3JldGUoDQogICJFbnZpcm9ubWVudCINCikNCmBgYA0KDQojIyMjIyBFbnRyb3B5LVJpY2huZXNzDQpgYGB7cn0NCnBsb3RseTo6cGxvdF9seShkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6ZmlsdGVyKEVudmlyb25tZW50IDwgMiksDQogICAgICAgICAgICAgICAgeCA9IH5SaWNobmVzcywgeSA9IH5FbnRyb3B5LCB6ID0gflRpbWUsIHR5cGUgPSAic2NhdHRlcjNkIiwNCiAgICAgICAgICAgICAgICBtb2RlID0gImxpbmVzIiwgb3BhY2l0eSA9IDEsIGxpbmUgPSBsaXN0KGNvbG9yID0gflRpbWUpKQ0KYGBgDQoNCiMjIyMjIEV2ZW5uZXNzDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRGl2ZXJzaXR5LCANCiAgZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFdmVubmVzcywNCiAgICBjb2xvciA9IGZhY3RvcihFbnZpcm9ubWVudCksDQogICAgYWxwaGEgPSBpZmVsc2UoRW52aXJvbm1lbnQgPT0gMSwgMSwgMC4zKQ0KICApDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCiAgZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIFRpbWUNCiAgICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgICAgRXZlbm5lc3MgPSBtZWFuKEV2ZW5uZXNzKQ0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEV2ZW5uZXNzDQogICksDQogIGNvbG9yID0gImJsYWNrIiwNCiAgaW5oZXJpdC5hZXMgPSBGQUxTRQ0KKSArIGdncGxvdDI6Omd1aWRlcygNCiAgYWxwaGEgPSAibm9uZSINCikgKyBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9kaXNjcmV0ZSgNCiAgIkVudmlyb25tZW50Ig0KKQ0KYGBgDQoNCiMjIyMjIEVudmlyb25tZW50IERpdmVyc2l0eQ0KDQpgYGB7cn0NCkVudkRpdmVyc2l0eSA8LSBsYXBwbHkoDQogICAgMTooKG5jb2wocmVzdWx0JEFidW5kYW5jZSkgLSAxKSAvIHJlc3VsdCROdW1FbnZpcm9ubWVudHMpLA0KICAgIGZ1bmN0aW9uKGksIGFidW5kLCBudW1TcGVjaWVzKSB7DQogICAgICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgICAgICBlbnYgPC0gYWJ1bmRbLCAxICsgaSArIG51bVNwZWNpZXMgKiAoMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzIC0gMSldDQogICAgICAgIHJpY2huZXNzIDwtIHJvd1N1bXMoZW52ICE9IDApDQogICAgICAgIGFidW5kU3VtIDwtIHJvd1N1bXMoZW52KQ0KICAgICAgICBlbnZpcm9ubWVudHMgPC0gYXBwbHkoDQogICAgICAgICAgICBlbnYsIE1BUkdJTiA9IDEsDQogICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgICAgICAgICAgdG9TdHJpbmcod2hpY2goeCA+IDApKQ0KICAgICAgICAgICAgfQ0KICAgICAgICApDQogICAgICAgIGRhdGEuZnJhbWUoVGltZSA9IHRpbWUsIA0KICAgICAgICAgICAgICAgICAgIFJpY2huZXNzID0gcmljaG5lc3MsIA0KICAgICAgICAgICAgICAgICAgIEFidW5kYW5jZSA9IGFidW5kU3VtLA0KICAgICAgICAgICAgICAgICAgIFNwZWNpZXMgPSBpLA0KICAgICAgICAgICAgICAgICAgIEVudmlyb25tZW50cyA9IGVudmlyb25tZW50cykNCiAgICB9LA0KICAgIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSwNCiAgICBudW1TcGVjaWVzID0gKG5jb2wocmVzdWx0JEFidW5kYW5jZSkgLSAxKSAvIHJlc3VsdCROdW1FbnZpcm9ubWVudHMNCikNCg0KRW52RGl2ZXJzaXR5IDwtIGRwbHlyOjpiaW5kX3Jvd3MoRW52RGl2ZXJzaXR5KQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBFbnZEaXZlcnNpdHkgJT4lIGRwbHlyOjpmaWx0ZXIoUmljaG5lc3MgPiAxKSwgDQogIGFlcyh4ID0gVGltZSwgeSA9IFJpY2huZXNzLCBjb2xvciA9IFNwZWNpZXMpDQopICsgZ2VvbV9wb2ludCgNCiAgYWxwaGEgPSAwLjAxLCBzaXplID0gMw0KKSArIGd1aWRlcygNCiAgY29sb3IgPSAibm9uZSINCikNCmBgYA0KDQpgYGB7cn0NCndpdGgoRW52RGl2ZXJzaXR5ICU+JSBtdXRhdGUoDQogIFRpbWUgPSBmbG9vcihUaW1lKQ0KICApICU+JSBncm91cF9ieSgNCiAgICBUaW1lLCBTcGVjaWVzDQogICAgKSAlPiUgc3VtbWFyaXNlKA0KICAgICAgUmljaG5lc3MgPSByb3VuZChtZWFuKFJpY2huZXNzKSksDQogICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICApLA0KICAgICB0YWJsZShTcGVjaWVzLCBSaWNobmVzcykpDQpgYGANCiMjIyMgQmV0YSBEaXZlcnNpdHkgey50YWJzZXR9DQoNCiMjIyMjIFByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzDQpgYGB7cn0NCkF2ZXJhZ2VkQWJ1bmRhbmNlIDwtIHJlc3VsdCRBYnVuZGFuY2UgJT4lIGRhdGEuZnJhbWUoDQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICB0aW1lID0gZmxvb3IodGltZS8xMDApKjEwMA0KKSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICB0aW1lDQopICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICBkcGx5cjo6YWNyb3NzKC5mbnMgPSB+IG1lYW4oLngpKQ0KKQ0KYGBgDQoNCmBgYHtyfQ0KUENBIDwtIHByY29tcChBdmVyYWdlZEFidW5kYW5jZSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLCANCiAgICAgICAgICAgICAgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSwgcmFuay4gPSAyNSkNCmBgYA0KDQpgYGB7cn0NCmhlYWQoc3VtbWFyeShQQ0EpJGltcG9ydGFuY2VbMiwgXSkNCmBgYA0KDQpgYGB7cn0gDQpzdW0oc3VtbWFyeShQQ0EpJGltcG9ydGFuY2VbMiwgXSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdDI6OmF1dG9wbG90KFBDQSwgbG9hZGluZ3MgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBBdmVyYWdlZEFidW5kYW5jZSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLA0KICAgICAgICAgICAgICAgICAgY29sb3VyID0gInRpbWUiKQ0KYGBgDQoNCmBgYHtyfQ0KQXZlcmFnZWRQQSA8LSByZXN1bHQkQWJ1bmRhbmNlICU+JSBkYXRhLmZyYW1lKA0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgdGltZSA9IGZsb29yKHRpbWUvMTAwKSoxMDANCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIGRwbHlyOjphY3Jvc3MoLmNvbHMgPSAhdGltZSwgLmZucyA9IH4gLnggPiAwKQ0KKSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICB0aW1lDQopICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICBkcGx5cjo6YWNyb3NzKC5mbnMgPSB+IG1lYW4oLngpKQ0KKQ0KYGBgDQoNCmBgYHtyfQ0KUENBUEEgPC0gcHJjb21wKEF2ZXJhZ2VkUEEgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwgDQogICAgICAgICAgICAgIGNlbnRlciA9IFRSVUUsIHNjYWxlLiA9IFRSVUUsIHJhbmsuID0gMjUpDQpgYGANCg0KYGBge3J9DQpoZWFkKHN1bW1hcnkoUENBUEEpJGltcG9ydGFuY2VbMiwgXSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdDI6OmF1dG9wbG90KFBDQVBBLCBsb2FkaW5ncyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IEF2ZXJhZ2VkUEEgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwNCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ0aW1lIikNCmBgYA0KDQpgYGB7cn0gDQpzdW0oc3VtbWFyeShQQ0EpJGltcG9ydGFuY2VbMiwgXSkNCmBgYCANCg0KYGBge3IgY2xlYW51cDN9DQppZnJtKEF2ZXJhZ2VkQWJ1bmRhbmNlKQ0KaWZybShBdmVyYWdlZFBBKQ0KaWZybShQQ0EpDQppZnJtKFBDQVBBKQ0KYGBgDQoNCiMjIyMjIEJldGEgRGl2ZXJzaXR5IE92ZXIgVGltZQ0KYGBge3J9DQpFbnZpcm9ubWVudHMgPC0gbGFwcGx5KA0KICAxOnJlc3VsdCROdW1FbnZpcm9ubWVudHMsDQogIGZ1bmN0aW9uKGksIGFidW5kLCBudW1TcGVjaWVzKSB7DQogICAgdGltZSA8LSBhYnVuZFssIDFdDQogICAgZW52IDwtIGFidW5kWywgMSArIDE6bnVtU3BlY2llcyArIG51bVNwZWNpZXMgKiAoaSAtIDEpXQ0KICAgIA0KICAgIHJldHZhbCA8LSBkYXRhLmZyYW1lKEVudmlyb25tZW50ID0gdG9TdHJpbmcoaSksDQogICAgICAgICAgICAgICAgICAgICAgICAgVGltZSA9IHRpbWUNCiAgICApDQogICAgDQogICAgcmV0dmFsIDwtIGNiaW5kKHJldHZhbCwgZW52KQ0KICAgIGNvbG5hbWVzKHJldHZhbCkgPC0gYygiRW52aXJvbm1lbnQiLCAiVGltZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIkJhc2FsIiwgMTozNCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIkNvbnN1bWVyIiwgMzU6MTAwKQ0KICAgICkNCiAgICByZXR1cm4ocmV0dmFsKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgfSwNCiAgYWJ1bmQgPSByZXN1bHQkQWJ1bmRhbmNlICU+JSBkYXRhLmZyYW1lKA0KICApICU+JSBkcGx5cjo6bXV0YXRlKA0KICAgIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQogICkgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgICB0aW1lDQogICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgZHBseXI6OmFjcm9zcyguZm5zID0gfiBtZWFuKC54KSkNCiAgKSwNCiAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCkVudmlyb25tZW50cyA8LSBkcGx5cjo6YmluZF9yb3dzKEVudmlyb25tZW50cykNCkVudmlyb25tZW50cyA8LSBFbnZpcm9ubWVudHMgJT4lIGRwbHlyOjpmaWx0ZXIoDQogIGRwbHlyOjppZl9hbnkoZHBseXI6OnN0YXJ0c193aXRoKGMoIkJhc2FsIiwgIkNvbnN1bWVyIikpLCB+ICgueCAhPSAwKSksDQopDQpgYGANCg0KIyMjIyMgQ2x1c3RlciBBbmFseXNpcyBPdmVyIFRpbWUNCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlIDwtIEVudmlyb25tZW50cyAlPiUgZHBseXI6OnNlbGVjdCgNCiAgLUVudmlyb25tZW50LCAtVGltZQ0KKSAlPiUgdmVnYW46OnZlZ2Rpc3QobWV0aG9kID0gImphY2NhcmQiKQ0KYGBgDQoNCmBgYHtyfQ0KRW52RGlzdENsdXN0IDwtIGhjbHVzdChFbnZpcm9ubWVudERpc3RhbmNlKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3QsIGxhYmVscyA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KRW52aXJvbm1lbnREaXN0YW5jZVBBIDwtIEVudmlyb25tZW50cyAlPiUgZHBseXI6OnNlbGVjdCgNCiAgLUVudmlyb25tZW50LCAtVGltZQ0KKSAlPiUgdmVnYW46OnZlZ2Rpc3QobWV0aG9kID0gImphY2NhcmQiLCBiaW5hcnkgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KRW52RGlzdENsdXN0UEEgPC0gaGNsdXN0KEVudmlyb25tZW50RGlzdGFuY2VQQSkNCmBgYA0KDQpgYGB7cn0NCnBsb3QoRW52RGlzdENsdXN0UEEsIGxhYmVscyA9IEZBTFNFKQ0KYGBgDQo=